replAction :: (ReplFlags, BuildExFlags) -> [String] -> GlobalFlags -> IO ()
replAction (replFlags, buildExFlags) extraArgs globalFlags = do
  cwd     <- getCurrentDirectory
  pkgDesc <- findPackageDesc cwd
  either (const onNoPkgDesc) (const onPkgDesc) pkgDesc
  where
    verbosity = fromFlagOrDefault normal (replVerbosity replFlags)

    -- There is a .cabal file in the current directory: start a REPL and load
    -- the project's modules.
    onPkgDesc = do
      let distPref    = fromFlagOrDefault (useDistPref defaultSetupScriptOptions)
                        (replDistPref replFlags)
          noAddSource = case replReload replFlags of
            Flag True -> SkipAddSourceDepsCheck
            _         -> fromFlagOrDefault DontSkipAddSourceDepsCheck
                         (buildOnly buildExFlags)
          progConf     = defaultProgramConfiguration
          setupOptions = defaultSetupScriptOptions
            { useCabalVersion = orLaterVersion $ Version [1,18,0] []
            , useDistPref     = distPref
            }
          replFlags'   = replFlags
            { replVerbosity = toFlag verbosity
            , replDistPref  = toFlag distPref
            }
      -- Calls 'configureAction' to do the real work, so nothing special has to
      -- be done to support sandboxes.
      (useSandbox, _config) <- reconfigure verbosity distPref
                               mempty [] globalFlags noAddSource NoFlag
                               (const Nothing)

      maybeWithSandboxDirOnSearchPath useSandbox $
        setupWrapper verbosity setupOptions Nothing
        (Cabal.replCommand progConf) (const replFlags') extraArgs

    -- No .cabal file in the current directory: just start the REPL (possibly
    -- using the sandbox package DB).
    onNoPkgDesc = do
      (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags
                               mempty
      let configFlags = savedConfigureFlags config
      (comp, _platform, programDb) <- configCompilerAux' configFlags
      programDb' <- reconfigurePrograms verbosity
                                        (replProgramPaths replFlags)
                                        (replProgramArgs replFlags)
                                        programDb
      startInterpreter verbosity programDb' comp (configPackageDB' configFlags)

-- | Re-configure the package in the current directory if needed. Deciding
-- when to reconfigure and with which options is convoluted:
--
-- If we are reconfiguring, we must always run @configure@ with the
-- verbosity option we are given; however, that a previous configuration
-- uses a different verbosity setting is not reason enough to reconfigure.
--
-- The package should be configured to use the same \"dist\" prefix as
-- given to the @build@ command, otherwise the build will probably
-- fail. Not only does this determine the \"dist\" prefix setting if we
-- need to reconfigure anyway, but an existing configuration should be
-- invalidated if its \"dist\" prefix differs.
--
-- If the package has never been configured (i.e., there is no
-- LocalBuildInfo), we must configure first, using the default options.
--
-- If the package has been configured, there will be a 'LocalBuildInfo'.
-- If there no package description file, we assume that the
-- 'PackageDescription' is up to date, though the configuration may need
-- to be updated for other reasons (see above). If there is a package
-- description file, and it has been modified since the 'LocalBuildInfo'
-- was generated, then we need to reconfigure.
--
-- The caller of this function may also have specific requirements
-- regarding the flags the last configuration used. For example,
-- 'testAction' requires that the package be configured with test suites
-- enabled. The caller may pass the required settings to this function
-- along with a function to check the validity of the saved 'ConfigFlags';
-- these required settings will be checked first upon determining that
-- a previous configuration exists.
replAction :: (ReplFlags, BuildExFlags) -> [String] -> GlobalFlags -> IO ()
replAction (replFlags, buildExFlags) extraArgs globalFlags = do
  cwd     <- getCurrentDirectory
  pkgDesc <- findPackageDesc cwd
  either (const onNoPkgDesc) (const onPkgDesc) pkgDesc
  where
    verbosity = fromFlagOrDefault normal (replVerbosity replFlags)

    -- There is a .cabal file in the current directory: start a REPL and load
    -- the project's modules.
    onPkgDesc = do
      let distPref    = fromFlagOrDefault (useDistPref defaultSetupScriptOptions)
                        (replDistPref replFlags)
          noAddSource = case replReload replFlags of
            Flag True -> SkipAddSourceDepsCheck
            _         -> fromFlagOrDefault DontSkipAddSourceDepsCheck
                         (buildOnly buildExFlags)
          progConf     = defaultProgramConfiguration
          setupOptions = defaultSetupScriptOptions
            { useCabalVersion = orLaterVersion $ Version [1,18,0] []
            , useDistPref     = distPref
            }
          replFlags'   = replFlags
            { replVerbosity = toFlag verbosity
            , replDistPref  = toFlag distPref
            }
      -- Calls 'configureAction' to do the real work, so nothing special has to
      -- be done to support sandboxes.
      (useSandbox, _config) <- reconfigure verbosity distPref
                               mempty [] globalFlags noAddSource NoFlag
                               (const Nothing)

      maybeWithSandboxDirOnSearchPath useSandbox $
        setupWrapper verbosity setupOptions Nothing
        (Cabal.replCommand progConf) (const replFlags') extraArgs

    -- No .cabal file in the current directory: just start the REPL (possibly
    -- using the sandbox package DB).
    onNoPkgDesc = do
      (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags
                               mempty
      let configFlags = savedConfigureFlags config
      (comp, _platform, programDb) <- configCompilerAux' configFlags
      programDb' <- reconfigurePrograms verbosity
                                        (replProgramPaths replFlags)
                                        (replProgramArgs replFlags)
                                        programDb
      startInterpreter verbosity programDb' comp (configPackageDB' configFlags)

-- | Re-configure the package in the current directory if needed. Deciding
-- when to reconfigure and with which options is convoluted:
--
-- If we are reconfiguring, we must always run @configure@ with the
-- verbosity option we are given; however, that a previous configuration
-- uses a different verbosity setting is not reason enough to reconfigure.
--
-- The package should be configured to use the same \"dist\" prefix as
-- given to the @build@ command, otherwise the build will probably
-- fail. Not only does this determine the \"dist\" prefix setting if we
-- need to reconfigure anyway, but an existing configuration should be
-- invalidated if its \"dist\" prefix differs.
--
-- If the package has never been configured (i.e., there is no
-- LocalBuildInfo), we must configure first, using the default options.
--
-- If the package has been configured, there will be a 'LocalBuildInfo'.
-- If there no package description file, we assume that the
-- 'PackageDescription' is up to date, though the configuration may need
-- to be updated for other reasons (see above). If there is a package
-- description file, and it has been modified since the 'LocalBuildInfo'
-- was generated, then we need to reconfigure.
--
-- The caller of this function may also have specific requirements
-- regarding the flags the last configuration used. For example,
-- 'testAction' requires that the package be configured with test suites
-- enabled. The caller may pass the required settings to this function
-- along with a function to check the validity of the saved 'ConfigFlags';
-- these required settings will be checked first upon determining that
-- a previous configuration exists.
replAction (replFlags, buildExFlags) extraArgs globalFlags = do
  cwd     <- getCurrentDirectory
  pkgDesc <- findPackageDesc cwd
  either (const onNoPkgDesc) (const onPkgDesc) pkgDesc
  where
    verbosity = fromFlagOrDefault normal (replVerbosity replFlags)

    -- There is a .cabal file in the current directory: start a REPL and load
    -- the project's modules.
    onPkgDesc = do
      let distPref    = fromFlagOrDefault (useDistPref defaultSetupScriptOptions)
                        (replDistPref replFlags)
          noAddSource = case replReload replFlags of
            Flag True -> SkipAddSourceDepsCheck
            _         -> fromFlagOrDefault DontSkipAddSourceDepsCheck
                         (buildOnly buildExFlags)
          progConf     = defaultProgramConfiguration
          setupOptions = defaultSetupScriptOptions
            { useCabalVersion = orLaterVersion $ Version [1,18,0] []
            , useDistPref     = distPref
            }
          replFlags'   = replFlags
            { replVerbosity = toFlag verbosity
            , replDistPref  = toFlag distPref
            }
      -- Calls 'configureAction' to do the real work, so nothing special has to
      -- be done to support sandboxes.
      (useSandbox, _config) <- reconfigure verbosity distPref
                               mempty [] globalFlags noAddSource NoFlag
                               (const Nothing)

      maybeWithSandboxDirOnSearchPath useSandbox $
        setupWrapper verbosity setupOptions Nothing
        (Cabal.replCommand progConf) (const replFlags') extraArgs

    -- No .cabal file in the current directory: just start the REPL (possibly
    -- using the sandbox package DB).
    onNoPkgDesc = do
      (_useSandbox, config) <- loadConfigOrSandboxConfig verbosity globalFlags
                               mempty
      let configFlags = savedConfigureFlags config
      (comp, _platform, programDb) <- configCompilerAux' configFlags
      programDb' <- reconfigurePrograms verbosity
                                        (replProgramPaths replFlags)
                                        (replProgramArgs replFlags)
                                        programDb
      startInterpreter verbosity programDb' comp (configPackageDB' configFlags)

-- | Re-configure the package in the current directory if needed. Deciding
-- when to reconfigure and with which options is convoluted:
--
-- If we are reconfiguring, we must always run @configure@ with the
-- verbosity option we are given; however, that a previous configuration
-- uses a different verbosity setting is not reason enough to reconfigure.
--
-- The package should be configured to use the same \"dist\" prefix as
-- given to the @build@ command, otherwise the build will probably
-- fail. Not only does this determine the \"dist\" prefix setting if we
-- need to reconfigure anyway, but an existing configuration should be
-- invalidated if its \"dist\" prefix differs.
--
-- If the package has never been configured (i.e., there is no
-- LocalBuildInfo), we must configure first, using the default options.
--
-- If the package has been configured, there will be a 'LocalBuildInfo'.
-- If there no package description file, we assume that the
-- 'PackageDescription' is up to date, though the configuration may need
-- to be updated for other reasons (see above). If there is a package
-- description file, and it has been modified since the 'LocalBuildInfo'
-- was generated, then we need to reconfigure.
--
-- The caller of this function may also have specific requirements
-- regarding the flags the last configuration used. For example,
-- 'testAction' requires that the package be configured with test suites
-- enabled. The caller may pass the required settings to this function
-- along with a function to check the validity of the saved 'ConfigFlags';
-- these required settings will be checked first upon determining that
-- a previous configuration exists.