# GHC Clippy Plugin [![Build status](https://img.shields.io/travis/ArturGajowy/ghc-clippy-plugin.svg?logo=travis)](https://travis-ci.org/ArturGajowy/ghc-clippy-plugin) [![License](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) A helpful companion to GHC. Overrides GHC error and warning messages, to the user's liking. Configured using (how else!) regexps. Tested with stack and ghcid. ## Showcase Left: without Clippy. Right: with Clippy, using the sample config. ## But, why? For all kinds of reasons: - making GHC messages more terse or more verbose - adding more context for beginners - stripping confusing / duplicated / rarely useful context for everyone - prototyping improvements for ghc error output - ever wanted GHC to speak in emoji? :smiling_imp: - ... or in your mother tongue? - ... or in mathematical notation? ## Usage 1. Add the `ghc-clippy-plugin` dependency to your project 2. Pass `-fplugin=Clippy` in GHC options E.g. for stack: ```yaml # file: package.yaml dependencies: - ghc-clippy-plugin ghc-options: - -fplugin=Clippy ``` 3. When building, you should see the following warning: ``` ghc-clippy-plugin: warning: Clippy plugin couldn't start. Cause: ./.clippy.dhall: openFile: does not exist (No such file or directory) ``` Make sure there was anything to compile (change a .hs file) if the warning wasn't there. Save the [sample config](/.clippy-terse.dhall) as `.clippy.dhall` in the project's root dir (or - more precisely - the `current directory` GHC is going to use) 4. Put an error somewhere: ```haskell oops = print mempty ``` With the sample config, this should output: ``` ./app/Main.hs:19:14-19: error: Type variable ‘a0’ is ambiguous in ‘mempty’. Can't pick an instance for ‘(Monoid a0)’. --- Maybe-fix: add type annotations to disambiguate. More info: compile with -fprint-potential-instances. | 19 | oops = print mempty | ``` 5. Enjoy the much terser output and tweak it to your heart's content! :grin: ## Tweaking the config ### Stack For `--file-watch` to pick up config changes, add in `package.yaml`: ```yaml extra-source-files: - .clippy.dhall ``` Use `stack build --file-watch`. Make sure to have some errors handy! :) ### Ghcid For ghcid to reload after config change, run it with `--reload=.clippy.dhall`. I tend to put the above line in `./.ghcid`. Ghcid may terminate if there are compile errors on startup. If that's the case, remove your errors until ghcid starts successfully :) With the above, for error messages, ghcid picks up `.clippy.dhall` changes immediately. For warnings, one needs to trigger recompilation of the file triggering them. One way around that is to enable `-Werror` for the period of config tweaking. (I put mine in `./.ghci`.) ### Error message structure, section markers In GHC, each error/warning message contains 3 sections in its [ErrDoc](https://hackage.haskell.org/package/ghc-8.10.1/docs/src/ErrUtils.html#ErrDoc) ('Important', 'Context', and 'Supplementary'). Each section contains a list of `MsgDoc`-s. Before replacing the message text, Clippy wraps each section, and each of their `MsgDoc`-s, with markers. This allows for more precise match targeting, including MsgDoc/section ends. To see the structure of the replaced error message, comment out the `marker removing rule` in the config. ```dhall rules = [ -- rule "(>>[ICS]>)|(<[ICS]<<)|(>[ICS]>)|(<[ICS]<)" "" , rule "..." "..." -- ... ] ``` Comment out all rules to see the structure of the original message. Sample result: ``` ./app/Main.hs:27:11: error: • >>I> >I> No instance for (Num a) arising from a use of ‘+’ Possible fix: add (Num a) to the context of the type signature for: bar :: forall a. a -> a -> a >C> >C> In the expression: a + b In an equation for ‘bar’: bar a b = a + b >S> >C>.*?