From 2091b7ed3486a72044ae80d2898421f517f43f20 Mon Sep 17 00:00:00 2001 From: Travis Cardwell Date: Wed, 22 Mar 2023 15:51:23 +0900 Subject: [PATCH] Add ob repl -c/--config option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option allows users to insert configuration into the `.ghci` file that is loaded by the REPL. It is up to the user to not insert commands that breaks things. Example user `.ghci` file: ``` :set prompt "\ESC[32;1mλ: \ESC[m" :set prompt-cont "\ESC[32;1m : \ESC[m" :set +t :set +m :set -XOverloadedStrings :set -XScopedTypeVariables :set -XTypeApplications ``` Example usage: ``` ob repl -c .ghci ``` --- ChangeLog.md | 1 + lib/command/src/Obelisk/Command.hs | 14 +++++++++++--- lib/command/src/Obelisk/Command/Run.hs | 19 +++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 6ef8457cd..829397229 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,6 +27,7 @@ This project's release branch is `master`. This log is written from the perspect * [#949](https://github.com/obsidiansystems/obelisk/pull/949): Obelisk now configures its output and error streams to transliterate unsupported characters to a known-good replacement character. * [#951](https://github.com/obsidiansystems/obelisk/pull/951): Add `ob run --port` which allows the user to override the port on which the app is served, rather than always using the port configured in the route. This is compatible with the use of reverse proxies that do their own TLS termination, e.g. `ngrok`. * [#974](https://github.com/obsidiansystems/obelisk/pull/974): Fail `ob run` when invalid fields are present in `default-extensions`/`default-language`, or when Cabal files are otherwise invalid. + * [#1025](https://github.com/obsidiansystems/obelisk/pull/1025): Add `-c/--config` option to `ob repl` * obelisk-route * [#915](https://github.com/obsidiansystems/obelisk/pull/915): Add `routeLinkAttr` to `Obelisk.Route.Frontend`. This allows the creation of route links with additional, user-specified DOM attributes. * [#918](https://github.com/obsidiansystems/obelisk/pull/918): Add GHC 8.10.7 support for `obelisk-route`. diff --git a/lib/command/src/Obelisk/Command.hs b/lib/command/src/Obelisk/Command.hs index 62a5d37ed..005bbf317 100644 --- a/lib/command/src/Obelisk/Command.hs +++ b/lib/command/src/Obelisk/Command.hs @@ -91,7 +91,7 @@ data ObCommand | ObCommand_Run [(FilePath, Interpret)] (Maybe FilePath) (Maybe PortNumber) | ObCommand_Profile String [String] | ObCommand_Thunk ThunkOption - | ObCommand_Repl [(FilePath, Interpret)] + | ObCommand_Repl (Maybe FilePath) [(FilePath, Interpret)] -- user @.ghci@ config | ObCommand_Watch [(FilePath, Interpret)] | ObCommand_Shell ShellOpts | ObCommand_Doc String [String] -- shell and list of packages @@ -120,7 +120,7 @@ obCommand cfg = hsubparser $ progDesc "Run current project in development mode" , command "profile" $ info (uncurry ObCommand_Profile <$> profileCommand) $ progDesc "Run current project with profiling enabled" , command "thunk" $ info (ObCommand_Thunk <$> thunkOption) $ progDesc "Manipulate thunk directories" - , command "repl" $ info (ObCommand_Repl <$> interpretOpts) $ progDesc "Open an interactive interpreter" + , command "repl" $ info (ObCommand_Repl <$> optional userGhciConfigOpt <*> interpretOpts) $ progDesc "Open an interactive interpreter" , command "watch" $ info (ObCommand_Watch <$> interpretOpts) $ progDesc "Watch current project for errors and warnings" , command "shell" $ info (ObCommand_Shell <$> shellOpts) $ progDesc "Enter a shell with project dependencies or run a command in such a shell. E.g. ob shell -- ghc-pkg list" , command "doc" $ info (ObCommand_Doc <$> shellFlags <*> packageNames) $ @@ -276,6 +276,14 @@ shellFlags = <|> flag "ghc" "ghcjs" (long "ghcjs" <> help "Enter a shell having ghcjs rather than ghc") <|> strOption (short 'A' <> long "argument" <> metavar "NIXARG" <> help "Use the environment specified by the given nix argument of `shells'") +userGhciConfigOpt :: Parser FilePath +userGhciConfigOpt = strOption $ mconcat + [ long "config" + , short 'c' + , metavar "CONFIG" + , help "User .ghci config file (use at your own risk)" + ] + interpretOpts :: Parser [(FilePath, Interpret)] interpretOpts = many ( (, Interpret_Interpret) <$> @@ -440,7 +448,7 @@ ob = \case ThunkCommand_Pack config -> for_ thunks (packThunk config) where thunks = _thunkOption_thunks to - ObCommand_Repl interpretPathsList -> withInterpretPaths interpretPathsList runRepl + ObCommand_Repl mUserGhciConfig interpretPathsList -> withInterpretPaths interpretPathsList $ runRepl mUserGhciConfig ObCommand_Watch interpretPathsList -> withInterpretPaths interpretPathsList runWatch ObCommand_Shell (ShellOpts shellAttr interpretPathsList cmd) -> withInterpretPaths interpretPathsList $ \root interpretPaths -> do putLog Notice "Hint: use '--no-interpret path/to/dependency' to force building an unpacked dependency and include it in this shell." diff --git a/lib/command/src/Obelisk/Command/Run.hs b/lib/command/src/Obelisk/Command/Run.hs index 0e2b6b3de..4204e0cbe 100644 --- a/lib/command/src/Obelisk/Command/Run.hs +++ b/lib/command/src/Obelisk/Command/Run.hs @@ -208,7 +208,7 @@ run certDir portOverride root interpretPaths = do _ -> pure () ghciArgs <- getGhciSessionSettings (pkgs <> manifestPkg) root freePort <- getFreePort - withGhciScriptArgs pkgs $ \dotGhciArgs -> do + withGhciScriptArgs [] pkgs $ \dotGhciArgs -> do runGhcid root True (ghciArgs <> dotGhciArgs) pkgs $ Just $ unwords [ "Obelisk.Run.run (Obelisk.Run.defaultRunApp" , "Backend.backend" @@ -220,18 +220,19 @@ run certDir portOverride root interpretPaths = do , "}" ] -runRepl :: MonadObelisk m => FilePath -> PathTree Interpret -> m () -runRepl root interpretPaths = do +runRepl :: MonadObelisk m => Maybe FilePath -> FilePath -> PathTree Interpret -> m () +runRepl mUserGhciConfig root interpretPaths = do pkgs <- getParsedLocalPkgs root interpretPaths ghciArgs <- getGhciSessionSettings pkgs root - withGhciScriptArgs pkgs $ \dotGhciArgs -> + userCommands <- maybe (pure []) (fmap lines . liftIO . readFile) mUserGhciConfig + withGhciScriptArgs userCommands pkgs $ \dotGhciArgs -> runGhciRepl root pkgs (ghciArgs <> dotGhciArgs) runWatch :: MonadObelisk m => FilePath -> PathTree Interpret -> m () runWatch root interpretPaths = do pkgs <- getParsedLocalPkgs root interpretPaths ghciArgs <- getGhciSessionSettings pkgs root - withGhciScriptArgs pkgs $ \dotGhciArgs -> + withGhciScriptArgs [] pkgs $ \dotGhciArgs -> runGhcid root True (ghciArgs <> dotGhciArgs) pkgs Nothing exportGhciConfig :: MonadObelisk m => FilePath -> PathTree Interpret -> m [String] @@ -452,11 +453,13 @@ packageInfoToNamePathMap = Map.fromList . map (_cabalPackageInfo_packageName &&& -- Like 'withGhciScript' but provides the precise ghci arguments to add to a ghci session withGhciScriptArgs :: (MonadObelisk m, Foldable f) - => f CabalPackageInfo -- ^ List of packages to load into ghci + => [String] -- ^ User commands to insert into .ghci + -> f CabalPackageInfo -- ^ List of packages to load into ghci -> ([String] -> m ()) -- ^ Action to run with the extra ghci arguments -> m () -withGhciScriptArgs packageInfos f = withGhciScript loadPreludeManually packageInfos $ \fp -> - f ["-XNoImplicitPrelude", "-ghci-script", fp] +withGhciScriptArgs userCommands packageInfos f = + withGhciScript (loadPreludeManually ++ userCommands) packageInfos $ \fp -> + f ["-XNoImplicitPrelude", "-ghci-script", fp] where -- These lines must be first and allow the session to support a custom Prelude when @-XNoImplicitPrelude@ -- is passed to the ghci session.