Commit 71cf94db authored by Fraser Tweedale's avatar Fraser Tweedale Committed by Marge Bot

GHCi: fix load order of .ghci files

Directives in .ghci files in the current directory ("local .ghci")
can be overridden by global files.  Change the order in which the
configs are loaded: global and $HOME/.ghci first, then local.

Also introduce a new field to GHCiState to control whether local
.ghci gets sourced or ignored.  This commit does not add a way to
set this value (a subsequent commit will add this), but the .ghci
sourcing routine respects its value.

Fixes: ghc/ghc#14689
Related: ghc/ghc#6017
Related: ghc/ghc#14250
parent 4b1ef06d
...@@ -102,7 +102,7 @@ import Data.Char ...@@ -102,7 +102,7 @@ import Data.Char
import Data.Function import Data.Function
import Data.IORef ( IORef, modifyIORef, newIORef, readIORef, writeIORef ) import Data.IORef ( IORef, modifyIORef, newIORef, readIORef, writeIORef )
import Data.List ( find, group, intercalate, intersperse, isPrefixOf, nub, import Data.List ( find, group, intercalate, intersperse, isPrefixOf, nub,
partition, sort, sortBy ) partition, sort, sortBy, (\\) )
import qualified Data.Set as S import qualified Data.Set as S
import Data.Maybe import Data.Maybe
import Data.Map (Map) import Data.Map (Map)
...@@ -482,6 +482,7 @@ interactiveUI config srcs maybe_exprs = do ...@@ -482,6 +482,7 @@ interactiveUI config srcs maybe_exprs = do
stop = default_stop, stop = default_stop,
editor = default_editor, editor = default_editor,
options = [], options = [],
localConfig = SourceLocalConfig,
-- We initialize line number as 0, not 1, because we use -- We initialize line number as 0, not 1, because we use
-- current line number while reporting errors which is -- current line number while reporting errors which is
-- incremented after reading a line. -- incremented after reading a line.
...@@ -566,8 +567,6 @@ runGHCi paths maybe_exprs = do ...@@ -566,8 +567,6 @@ runGHCi paths maybe_exprs = do
let let
ignore_dot_ghci = gopt Opt_IgnoreDotGhci dflags ignore_dot_ghci = gopt Opt_IgnoreDotGhci dflags
current_dir = return (Just ".ghci")
app_user_dir = liftIO $ withGhcAppData app_user_dir = liftIO $ withGhcAppData
(\dir -> return (Just (dir </> "ghci.conf"))) (\dir -> return (Just (dir </> "ghci.conf")))
(return Nothing) (return Nothing)
...@@ -606,17 +605,44 @@ runGHCi paths maybe_exprs = do ...@@ -606,17 +605,44 @@ runGHCi paths maybe_exprs = do
setGHCContextFromGHCiState setGHCContextFromGHCiState
dot_cfgs <- if ignore_dot_ghci then return [] else do processedCfgs <- if ignore_dot_ghci
dot_files <- catMaybes <$> sequence [ current_dir, app_user_dir, home_dir ] then pure []
liftIO $ filterM checkFileAndDirPerms dot_files else do
mdot_cfgs <- liftIO $ mapM canonicalizePath' dot_cfgs userCfgs <- do
paths <- catMaybes <$> sequence [ app_user_dir, home_dir ]
checkedPaths <- liftIO $ filterM checkFileAndDirPerms paths
liftIO . fmap (nub . catMaybes) $ mapM canonicalizePath' checkedPaths
localCfg <- do
let path = ".ghci"
ok <- liftIO $ checkFileAndDirPerms path
if ok then liftIO $ canonicalizePath' path else pure Nothing
mapM_ sourceConfigFile userCfgs
-- Process the global and user .ghci
-- (but not $CWD/.ghci or CLI args, yet)
behaviour <- localConfig <$> getGHCiState
processedLocalCfg <- case localCfg of
Just path | path `notElem` userCfgs ->
-- don't read .ghci twice if CWD is $HOME
case behaviour of
SourceLocalConfig -> localCfg <$ sourceConfigFile path
IgnoreLocalConfig -> pure Nothing
_ -> pure Nothing
pure $ maybe id (:) processedLocalCfg userCfgs
let arg_cfgs = reverse $ ghciScripts dflags let arg_cfgs = reverse $ ghciScripts dflags
-- -ghci-script are collected in reverse order -- -ghci-script are collected in reverse order
-- We don't require that a script explicitly added by -ghci-script -- We don't require that a script explicitly added by -ghci-script
-- is owned by the current user. (#6017) -- is owned by the current user. (#6017)
mapM_ sourceConfigFile $ nub $ (catMaybes mdot_cfgs) ++ arg_cfgs
-- nub, because we don't want to read .ghci twice if the CWD is $HOME. mapM_ sourceConfigFile $ nub arg_cfgs \\ processedCfgs
-- Dedup, and remove any configs we already processed.
-- Importantly, if $PWD/.ghci was ignored due to configuration,
-- explicitly specifying it does cause it to be processed.
-- Perform a :load for files given on the GHCi command line -- Perform a :load for files given on the GHCi command line
-- When in -e mode, if the load fails then we want to stop -- When in -e mode, if the load fails then we want to stop
......
...@@ -15,6 +15,7 @@ module GHCi.UI.Monad ( ...@@ -15,6 +15,7 @@ module GHCi.UI.Monad (
GHCiState(..), GhciMonad(..), GHCiState(..), GhciMonad(..),
GHCiOption(..), isOptionSet, setOption, unsetOption, GHCiOption(..), isOptionSet, setOption, unsetOption,
Command(..), CommandResult(..), cmdSuccess, Command(..), CommandResult(..), cmdSuccess,
LocalConfigBehaviour(..),
PromptFunction, PromptFunction,
BreakLocation(..), BreakLocation(..),
TickArray, TickArray,
...@@ -79,6 +80,7 @@ data GHCiState = GHCiState ...@@ -79,6 +80,7 @@ data GHCiState = GHCiState
prompt_cont :: PromptFunction, prompt_cont :: PromptFunction,
editor :: String, editor :: String,
stop :: String, stop :: String,
localConfig :: LocalConfigBehaviour,
options :: [GHCiOption], options :: [GHCiOption],
line_number :: !Int, -- ^ input line line_number :: !Int, -- ^ input line
break_ctr :: !Int, break_ctr :: !Int,
...@@ -197,6 +199,15 @@ data GHCiOption ...@@ -197,6 +199,15 @@ data GHCiOption
-- modules after load -- modules after load
deriving Eq deriving Eq
-- | Treatment of ./.ghci files. For now we either load or
-- ignore. But later we could implement a "safe mode" where
-- only safe operations are performed.
--
data LocalConfigBehaviour
= SourceLocalConfig
| IgnoreLocalConfig
deriving (Eq)
data BreakLocation data BreakLocation
= BreakLocation = BreakLocation
{ breakModule :: !GHC.Module { breakModule :: !GHC.Module
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment