From 2c4b72ca870559134a234831c6c59563a34631d9 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Mon, 20 May 2024 17:10:18 -0400 Subject: [PATCH] Add user manual (#250) ~~Blocked on #248~~ Adds the actual user manual mdBook, but doesn't build or publish it. --- docs/.gitignore | 1 + docs/SUMMARY.md | 9 + docs/book.toml | 14 ++ docs/cli.md | 366 +++++++++++++++++++++++++++++++++++++ docs/comment-evaluation.md | 77 ++++++++ docs/getting-started.md | 18 ++ docs/install.md | 41 +++++ docs/introduction.md | 41 +++++ docs/lifecycle-hooks.md | 152 +++++++++++++++ docs/no-load.md | 70 +++++++ nix/packages/ghciwatch.nix | 2 + 11 files changed, 791 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/SUMMARY.md create mode 100644 docs/book.toml create mode 100644 docs/cli.md create mode 100644 docs/comment-evaluation.md create mode 100644 docs/getting-started.md create mode 100644 docs/install.md create mode 100644 docs/introduction.md create mode 100644 docs/lifecycle-hooks.md create mode 100644 docs/no-load.md diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..5a0bf031 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +/book diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 00000000..5229ded0 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,9 @@ +# Summary + +- [Introduction](./introduction.md) +- [Installation](./install.md) +- [Getting started](./getting-started.md) +- [Command-line arguments](./cli.md) +- [Lifecycle hooks](./lifecycle-hooks.md) +- [Comment evaluation](./comment-evaluation.md) +- [Only load modules you need](./no-load.md) diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 00000000..26eb73a1 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,14 @@ +[book] +authors = [ + "Rebecca Turner", +] +language = "en" +multilingual = false +src = "." +title = "ghciwatch" + +[output.html] +curly-quotes = true +git-repository-url = "https://github.com/MercuryTechnologies/ghciwatch" +git-repository-icon = "fa-github" +edit-url-template = "https://github.com/MercuryTechnologies/ghciwatch/edit/main/docs/{path}" diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 00000000..291cac7b --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,366 @@ +# Command-line arguments for `ghciwatch` + +Ghciwatch loads a GHCi session for a Haskell project and reloads it +when source files change. + +**Usage:** `ghciwatch [--command SHELL_COMMAND] [--watch PATH] [OPTIONS ...]` + + + +## Examples + +Load `cabal v2-repl` and watch for changes in `src`: + + ghciwatch + +Load a custom GHCi session and watch for changes in multiple locations: + + ghciwatch --command "cabal v2-repl lib:test-dev" \ + --watch src --watch test + +Run tests after reloads: + + ghciwatch --test-ghci TestMain.testMain \ + --after-startup-ghci ':set args "--match=/OnlyRunSomeTests/"' + +Use `hpack` to regenerate `.cabal` files: + + ghciwatch --before-startup-shell hpack \ + --restart-glob '**/package.yaml' + +Also reload the session when `.persistentmodels` change: + + ghciwatch --watch config/modelsFiles \ + --reload-glob '**/*.persistentmodels' + +Don't reload for `README.md` files: + + ghciwatch --reload-glob '!src/**/README.md' + +## Options +
+ +
--command <SHELL_COMMAND>
+ +A shell command which starts a `ghci` REPL, e.g. `ghci` or `cabal v2-repl` or similar. + +This is used to launch the underlying `ghci` session that `ghciwatch` controls. + +May contain quoted arguments which will be parsed in a `sh`-like manner. + +
+
--error-file <ERROR_FILE>
+ +A file to write compilation errors to. + +The output format is compatible with `ghcid`'s `--outputfile` option. + +
+
--enable-eval
+ +Evaluate Haskell code in comments. + +This parses line commands starting with `-- $>` or multiline commands delimited by `{- $>` and `<$ -}` and evaluates them after reloads. + +
+
--clear
+ +Clear the screen before reloads and restarts + +
+
--no-interrupt-reloads
+ +Don't interrupt reloads when files change. + +Depending on your workflow, `ghciwatch` may feel more responsive with this set. + +
+ +
+ +## Lifecycle hooks +
+ +
--test-ghci <GHCI_CMD>
+ +`ghci` commands to run tests + +Tests are run after startup and after reloads. + +Example: `TestMain.testMain`. + +Can be given multiple times. + +
+
--test-shell <SHELL_CMD>
+ +Shell commands to run tests + +Tests are run after startup and after reloads. + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+
--before-startup-shell <SHELL_CMD>
+ +Shell commands to run before startup + +Startup hooks run when `ghci` is started (at `ghciwatch` startup and after `ghci` restarts). + +Commands starting with `async:` will be run in the background. + +This can be used to regenerate `.cabal` files with `hpack`. + +Can be given multiple times. + +
+
--after-startup-ghci <GHCI_CMD>
+ +`ghci` commands to run after startup + +Startup hooks run when `ghci` is started (at `ghciwatch` startup and after `ghci` restarts). + +Use `:set args ...` to set command-line arguments for test hooks. + +Can be given multiple times. + +
+
--after-startup-shell <SHELL_CMD>
+ +Shell commands to run after startup + +Startup hooks run when `ghci` is started (at `ghciwatch` startup and after `ghci` restarts). + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+
--before-reload-ghci <GHCI_CMD>
+ +`ghci` commands to run before reload + +Reload hooks are run when modules are changed on disk. + +Can be given multiple times. + +
+
--before-reload-shell <SHELL_CMD>
+ +Shell commands to run before reload + +Reload hooks are run when modules are changed on disk. + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+
--after-reload-ghci <GHCI_CMD>
+ +`ghci` commands to run after reload + +Reload hooks are run when modules are changed on disk. + +Can be given multiple times. + +
+
--after-reload-shell <SHELL_CMD>
+ +Shell commands to run after reload + +Reload hooks are run when modules are changed on disk. + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+
--before-restart-ghci <GHCI_CMD>
+ +`ghci` commands to run before restart + +Due to [a `ghci` bug][1], the `ghci` session must be restarted when Haskell modules +are removed or renamed. + +[1]: https://gitlab.haskell.org/ghc/ghc/-/issues/11596 + +Can be given multiple times. + +
+
--before-restart-shell <SHELL_CMD>
+ +Shell commands to run before restart + +Due to [a `ghci` bug][1], the `ghci` session must be restarted when Haskell modules +are removed or renamed. + +[1]: https://gitlab.haskell.org/ghc/ghc/-/issues/11596 + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+
--after-restart-ghci <GHCI_CMD>
+ +`ghci` commands to run after restart + +Due to [a `ghci` bug][1], the `ghci` session must be restarted when Haskell modules +are removed or renamed. + +[1]: https://gitlab.haskell.org/ghc/ghc/-/issues/11596 + +Can be given multiple times. + +
+
--after-restart-shell <SHELL_CMD>
+ +Shell commands to run after restart + +Due to [a `ghci` bug][1], the `ghci` session must be restarted when Haskell modules +are removed or renamed. + +[1]: https://gitlab.haskell.org/ghc/ghc/-/issues/11596 + +Commands starting with `async:` will be run in the background. + +Can be given multiple times. + +
+ +
+ +## File watching options +
+ +
--poll <DURATION>
+ +Use polling with the given interval rather than notification-based file watching. + +Polling tends to be more reliable and less performant. In particular, notification-based watching often misses updates on macOS. + +
+
--debounce <DURATION>
+ +Debounce file events; wait this duration after receiving an event before attempting to reload. + +Defaults to 0.5 seconds. + + Default value: `500ms` + +
+
--watch <PATH>
+ +A path to watch for changes. + +Directories are watched recursively. Can be given multiple times. + +
+
--reload-glob <RELOAD_GLOBS>
+ +Reload the `ghci` session when paths matching this glob change. + +By default, only changes to Haskell source files trigger reloads. If you'd like to exclude some files from that, you can add an ignore glob here, like `!src/my-special-dir/**/*.hs`. + +Globs provided here have precisely the same semantics as a single line in a `gitignore` file (`man gitignore`), where the meaning of `!` is inverted: namely, `!` at the beginning of a glob will ignore a file. + +The last matching glob will determine if a reload is triggered. + +Can be given multiple times. + +
+
--restart-glob <RESTART_GLOBS>
+ +Restart the `ghci` session when paths matching this glob change. + +By default, only changes to `.cabal` or `.ghci` files or Haskell source files being moved/removed will trigger restarts. + +Due to [a `ghci` bug][1], the `ghci` session must be restarted when Haskell modules are removed or renamed. + +See `--reload-globs` for more details. + +Can be given multiple times. + +[1]: https://gitlab.haskell.org/ghc/ghc/-/issues/11596 + +
+ +
+ +## Logging options +
+ +
--log-filter <LOG_FILTER>
+ +Log message filter. + +Can be any of "error", "warn", "info", "debug", or "trace". Supports more granular filtering, as well. + +The grammar is: `target[span{field=value}]=level`, where `target` is a module path, `span` is a span name, and `level` is one of the levels listed above. + +See [documentation in `tracing-subscriber`][1]. + +A nice value is `ghciwatch=debug`. + +[1]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html + + Default value: `ghciwatch=info` + +
+
--backtrace <BACKTRACE>
+ +How to display backtraces in error messages + + Default value: `0` + + Possible values: + - `0`: + Hide backtraces in errors + - `1`: + Display backtraces in errors + - `full`: + Display backtraces with all stack frames in errors + + +
+
--trace-spans <TRACE_SPANS>
+ +When to log span events, which loosely correspond to tasks being run in the async runtime. + +Allows multiple values, comma-separated. + + Default value: `new,close` + + Possible values: + - `new`: + Log when spans are created + - `enter`: + Log when spans are entered + - `exit`: + Log when spans are exited + - `close`: + Log when spans are dropped + - `none`: + Do not log span events + - `active`: + Log when spans are entered/exited + - `full`: + Log all span events + + +
+
--log-json <PATH>
+ +Path to write JSON logs to. + +JSON logs are not yet stable and the format may change on any release. + +
+ +
+ + + diff --git a/docs/comment-evaluation.md b/docs/comment-evaluation.md new file mode 100644 index 00000000..3accdca6 --- /dev/null +++ b/docs/comment-evaluation.md @@ -0,0 +1,77 @@ +# Comment evaluation + +With the [`--enable-eval`](cli.md#--enable-eval) flag set, ghciwatch will +execute Haskell code in comments which start with `$>` in GHCi. + +```haskell +myGreeting :: String +myGreeting = "Hello" + +-- $> putStrLn (myGreeting <> " " <> myGreeting) +``` + +Prints: + +``` +• src/MyLib.hs:9:7: putStrLn (myGreeting <> " " <> myGreeting) +Hello Hello +``` + +## Running tests with eval comments + +Eval comments can be used to run tests in a single file on reload. For large +test suites (thousands of tests), this can be much faster than using [Hspec's +`--match` option][hspec-match], because `--match` has to load the entire test +suite and perform string matches on `[Char]` to determine which tests should be +run. (Combine this with Cabal's [`--repl-no-load`](no-load.md) option to only +load the modules your test depends on for even faster reloads.) + +```haskell +module MyLibSpec (spec) where + +import Test.Hspec +import MyLib (myGreeting) + +-- $> import Test.Hspec -- May be necessary for some setups. +-- $> hspec spec + +spec :: Spec +spec = do + describe "myGreeting" $ do + it "is hello" $ do + myGreeting `shouldBe` "Hello" +``` + + +[hspec-match]: https://hspec.github.io/match.html +[test.hspec]: https://hackage.haskell.org/package/hspec/docs/Test-Hspec.html +[spec]: https://hackage.haskell.org/package/hspec/docs/Test-Hspec.html#t:Spec + +## Grammar + +Single-line eval comments have the following grammar: + +``` +[ \t]* # Leading whitespace +"-- $> " # Eval comment marker +[^\n]+ \n # Rest of line +``` + +Multi-line eval comments have the following grammar: + +``` +[ \t]* # Leading whitespace +"{- $>" # Eval comment marker +([ \t]* \n)? # Optional newline +([^\n]* \n)* # Lines of Haskell code +[ \t]* # Optional whitespace +"<$ -}" # Eval comment end marker +``` + + +## Performance implications + +Note that because each loaded module must be read (and re-read when it changes) +to parse eval comments, enabling this feature has some performance overhead. +(It's probably not too bad, because all those files are in your disk cache +anyways from being compiled by GHCi.) diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..d6837dc9 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,18 @@ +## Getting started + +To start a ghciwatch session, you'll need a command to start a GHCi session +(like `cabal repl`) and a set of paths and directories to watch for changes. +For example: + + ghciwatch --command "cabal repl lib:test-dev" \ + --watch src --watch test + +Check out the [examples](cli.md#examples) and [command-line +arguments](cli.md#options) for more information. + +Ghciwatch can [run test suites](cli.md#--test-ghci) after reloads, [evaluate +code in comments](cli.md#--enable-eval), [log compiler errors to a +file](cli.md#--error-file), run [startup hooks](cli.md#--before-startup-shell) +like [`hpack`][hpack] to generate `.cabal` files, and more! + +[hpack]: https://github.com/sol/hpack diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 00000000..c4fba524 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,41 @@ +# Installation + + +Packaging status + + +## Nixpkgs + +Ghciwatch is [available in `nixpkgs` as `ghciwatch`][nixpkgs]: + +```shell +nix-env -iA ghciwatch +nix profile install nixpkgs#ghciwatch +# Or add to your `/etc/nixos/configuration.nix`. +``` + +[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/gh/ghciwatch/package.nix + +## Statically-linked binaries + +Statically-linked binaries for aarch64/x86_64 macOS/Linux can be downloaded +from the [GitHub releases][latest]. + +[latest]: https://github.com/MercuryTechnologies/ghciwatch/releases/latest + +## Crates.io + +The Rust crate can be downloaded from [crates.io][crate]: + +```shell +cargo install ghciwatch +``` + +[crate]: https://crates.io/crates/ghciwatch + +## Hackage + +Ghciwatch is not yet available on [Hackage][hackage]; see [issue #23][issue-23]. + +[hackage]: https://hackage.haskell.org/ +[issue-23]: https://github.com/MercuryTechnologies/ghciwatch/issues/23 diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 00000000..45752450 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,41 @@ +# ghciwatch + +Ghciwatch loads a [GHCi][ghci] session for a Haskell project and reloads it +when source files change. + +[ghci]: https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html + +## Features + +- Ghciwatch can [clear the screen between reloads](cli.md#--clear). +- Compilation errors can be written to a file with [`--error-file`](cli.md#--error-file), for + compatibility with [ghcid's][ghcid] `--outputfile` option. +- Comments starting with `-- $>` [can be evaluated](comment-evaluation.md) in + GHCi. + - Eval comments have access to the top-level bindings of the module they're + defined in, including unexported bindings. + - Multi-line eval comments are supported with `{- $> ... <$ -}`. +- A variety of [lifecycle hooks](lilifecycle-hooks.md) let you run Haskell code + or shell commands on a variety of events. + - Run a test suite with [`--test-ghci + TestMain.testMain`](cli.md#--test-ghci). + - Refresh your `.cabal` files with [`hpack`][hpack] before GHCi starts using + [`--before-startup-shell hpack`](cli.md#--before-startup-shell). + - Format your code asynchronously using [`--before-reload-shell + async:fourmolu`](cli.md#--before-reload-shell). +- [Custom globs](cli.md#--reload-glob) can be supplied to reload or restart the + GHCi session when non-Haskell files (like templates or database schema + definitions) change. +- Ghciwatch can handle new modules, removed modules, or moved modules without a + hitch, so you don't need to manually restart it. + +[ghcid]: https://github.com/ndmitchell/ghcid +[hpack]: https://github.com/sol/hpack + +## Demo + +Check out an [asciinema demo][asciinema] to see how ghciwatch feels in practice: + + + +[asciinema]: https://asciinema.org/a/659712 diff --git a/docs/lifecycle-hooks.md b/docs/lifecycle-hooks.md new file mode 100644 index 00000000..3f9861a1 --- /dev/null +++ b/docs/lifecycle-hooks.md @@ -0,0 +1,152 @@ +# Lifecycle hooks + +Ghciwatch supports a number of [lifecycle hook options](cli.md#lifecycle-hooks) +like [`--test-ghci`](cli.md#--test-ghci), +[`--before-startup-shell`](cli.md#--before-startup-shell), and +[`--after-restart-ghci`](cli.md#--after-restart-ghci). + +Lifecycle hooks can be defined multiple times and run in sequence. For example: + + + ghciwatch --test-ghci TestMain.testMain \ + --test-ghci 'if myGreeting /= "Hello, world!" then error else ()' + +This command will first run `TestMain.testMain` and then the check for +`myGreeting`. + + +## Types of hooks + +Lifecycle hooks come in two main variants: shell commands and GHCi commands. + +### GHCi commands + +GHCi lifecycle hook options (like [`--test-ghci`](cli.md#--test-ghci) and +[`--after-startup-ghci`](cli.md#--after-startup-ghci)) end in `-ghci` and +define a command to be executed in the GHCi session. + +When running a test suite, you can use a hook like `--after-startup-ghci ':set +args "--match=/MyModule/"'` to [filter HSpec items][hspec-match] or otherwise +set command-line arguments for the test suite. + +[hspec-match]: https://hspec.github.io/match.html + +Note that any GHCi command is allowed, so there's nothing to stop you from +setting a hook like `:set prompt λ>` that breaks ghciwatch's ability to detect +when reloads are complete. + +Output printed by GHCi, including by GHCi lifecycle hooks, is printed to +ghciwatch's stdout. + +### Shell commands + +Shell lifecycle hook options (like [`--test-shell`](cli.md#--test-shell)) end +in `-shell` and define a shell command to be executed. + +Arguments can be quoted with standard `sh` syntax as defined in [POSIX.1-2008 +§2.2][sh-quoting] (however, note that no variable expansion is performed). + +If a shell lifecycle hook begins with `async:`, as in `--after-reload-shell +'async:tags'`, the command will be run asynchronously and ghciwatch will +continue to execute as normal. + +If a shell lifecycle hook fails (exits with a non-zero status code), a message +indicating the command that failed and the contents of its standard output and +standard error streams will be printed. + +[sh-quoting]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html + + +## Detecting if code is running in ghciwatch + +Before launching the GHCi session, ghciwatch sets the `IN_GHCIWATCH` +environment variable. GHCi and shell command lifecycle hooks can read this +environment variable to determine if they're being run inside a ghciwatch session. + +This is particularly useful for code which may be compiled, run in a plain +`ghci` session, or run in a ghciwatch-managed GHCi session. + + +## List of lifecycle hooks + +### Before startup + +Hook: [`--before-startup-shell`](cli.md#--before-startup-shell). + +When: Before the [`--command`](cli.md#--command) is executed to spawn a GHCi +session. + +No GHCi session exists when this hook is run, so only a shell hook is +available. + +Good for running tools like [`hpack`][hpack] to generate `.cabal` files. + +[hpack]: https://github.com/sol/hpack + + +### After startup + +Hooks: [`--after-startup-shell`](cli.md#--after-startup-shell), +[`--after-startup-ghci`](cli.md#--after-startup-ghci). + +When: After the [`--command`](cli.md#--command) executed to spawn a GHCi +session has finished loading and the [error log](cli.md#--error-file) has been +written, but before [eval commands](comment-evaluation.md) and [test +suites](#test) are executed. + +### Test + +Hooks: [`--test-shell`](cli.md#--test-shell), +[`--test-ghci`](cli.md#--test-ghci). + +When: After the GHCi session [starts up](#after-startup) or a +[reload](#after-reload) or [restart](#after-restart) completes. + +Note that if compilation fails, test suites and [eval +commands](comment-evaluation.md) will not run. + +### Before reload + +Hooks: [`--before-reload-shell`](cli.md#--before-reload-shell), +[`--before-reload-ghci`](cli.md#--before-reload-ghci). + +When: After file changes are detected but before a `:reload` or `:add` command +is sent to the GHCi session. + +Note that the before-reload hooks are not executed [before a +restart](#before-restart). + +### After reload + +Hooks: [`--after-reload-shell`](cli.md#--after-reload-shell), +[`--after-reload-ghci`](cli.md#--after-reload-ghci). + +When: After a reload has completed, after the [error log](cli.md#--error-file) +has been written, but before [eval commands](comment-evaluation.md) and [test +suites](#test) are executed. + +### Before restart + +Hooks: [`--before-restart-shell`](cli.md#--before-restart-shell), +[`--before-restart-ghci`](cli.md#--before-restart-ghci). + +When: After file changes that require a restart are detected but before the +GHCi session is `SIGKILL`ed. + +The GHCi session is restarted when `.cabal` files change, when Haskell modules +are deleted or moved, or when any files specified by +[`--restart-globs`](cli.md#--restart-globs) are changed. + +### After restart + +Hooks: [`--after-restart-shell`](cli.md#--after-restart-shell), +[`--after-restart-ghci`](cli.md#--after-restart-ghci). + +When: After the GHCi session has been restarted, the [after +startup](#after-startup) hooks have run, and after [eval +commands](comment-evaluation.md) and [test suites](#test) are executed. + +In the future, these hooks may run before eval commands and test suites are +executed (see [#242][242]). + +[242]: https://github.com/MercuryTechnologies/ghciwatch/issues/242 diff --git a/docs/no-load.md b/docs/no-load.md new file mode 100644 index 00000000..7b81e755 --- /dev/null +++ b/docs/no-load.md @@ -0,0 +1,70 @@ +# Only load modules you need + +**TL;DR:** Use [`cabal repl --repl-no-load`][repl-no-load] to start a GHCi +session with no modules loaded. Then, when you edit a module, ghciwatch will +`:add` it to the GHCi session, causing only the modules you need (and their +dependencies) to be loaded. In large projects, this can significantly cut down +on reload times. + +[repl-no-load]: https://cabal.readthedocs.io/en/stable/cabal-commands.html#cmdoption-repl-no-load + +## `--repl-no-load` in ghciwatch + +Ghciwatch supports `--repl-no-load` natively. Add `--repl-no-load` to the +[`ghciwatch --command`](cli.md#--command) option and ghciwatch will start a +GHCi session with no modules loaded. Then, edit a file and ghciwatch will load +it (and its dependencies) into the REPL. (Note that because no modules are +loaded initially, no compilation errors will show up until you start writing +files.) + +## `--repl-no-load` explained + +When you load a GHCi session with [`cabal repl`][cabal-repl], Cabal will +interpret and load all the modules in the specified target before presenting a +prompt: + +[cabal-repl]: https://cabal.readthedocs.io/en/stable/cabal-commands.html#cabal-repl + +``` +$ cabal repl test-dev +Build profile: -w ghc-9.0.2 -O1 +In order, the following will be built (use -v for more details): + - my-simple-package-0.1.0.0 (lib:test-dev) (first run) +Configuring library 'test-dev' for my-simple-package-0.1.0.0.. +Preprocessing library 'test-dev' for my-simple-package-0.1.0.0.. +GHCi, version 9.0.2: https://www.haskell.org/ghc/ :? for help +[1 of 3] Compiling MyLib ( src/MyLib.hs, interpreted ) +[2 of 3] Compiling MyModule ( src/MyModule.hs, interpreted ) +[3 of 3] Compiling TestMain ( test/TestMain.hs, interpreted ) +Ok, three modules loaded. +ghci> +``` + +For this toy project with three modules, that's not an issue, but it can start +to add up with larger projects: + +``` +$ echo :quit | time cabal repl +... +Ok, 9194 modules loaded. +ghci> Leaving GHCi. +________________________________________________________ +Executed in 161.07 secs +``` + +Fortunately, `cabal repl` includes a [`--repl-no-load`][repl-no-load] option +which instructs Cabal to skip interpreting or loading _any_ modules until it's +instructed to do so: + +``` +$ echo ":quit" | time cabal repl --repl-no-load +... +ghci> Leaving GHCi. +________________________________________________________ +Executed in 11.41 secs +``` + +Then, you can load modules into the empty GHCi session by `:add`ing them, and +only the specified modules and their dependencies will be interpreted. If you +only need to edit a small portion of a library's total modules, this can +provide a significantly faster workflow than loading every module up-front. diff --git a/nix/packages/ghciwatch.nix b/nix/packages/ghciwatch.nix index 3ebcae79..c15add7b 100644 --- a/nix/packages/ghciwatch.nix +++ b/nix/packages/ghciwatch.nix @@ -11,6 +11,7 @@ inputs, rustPlatform, rust-analyzer, + mdbook, # Versions of GHC to include in the environment for integration tests. # These should be attributes of `haskell.compiler`. ghcVersions ? null, @@ -152,6 +153,7 @@ # Extra development tools (cargo and rustc are included by default). packages = [ rust-analyzer + mdbook ]; }; in