NOTICE: Indentation has temporarily been disabled by default. SEE THE INDENTATION SECTION for why this was needed and what the plan is. TLDR indentation is going through a full rewrite.
Please contribute ideas on how the new indentation system would work. See rewriting the indentation section.
A Haskell mode that uses Tree-sitter.
The above screenshot is indented and coloured using haskell-ts-mode,
with prettify-symbols-mode enabled.
C-c C-rOpen REPLC-c C-cSend code to REPLM-qIndent the function
Overview of features:
- Syntax highlighting
- Structural navigation
- Indentation (now disabled)
- Imenu support
- REPL (
C-c C-rin the mode to run) - Prettify Symbols mode support
The more interesting features are:
- Logical syntax highlighting:
- Only arguments that can be used in functions are highlighted, e.g.,
in
f (_:(a:[]))onlyais highlighted, as it is the only variable that is captured, and that can be used in the body of the function. - The return type of a function is highlighted.
- All new variabels are (or should be) highlighted, this includes generators, lambda arguments.
- Highlighting the === operator in guarded matches correctly, this would be stupidly hard in regexp based syntax.
- Only arguments that can be used in functions are highlighted, e.g.,
in
- More performant, this is especially seen in longer files.
- Much, much less code,
haskell-modehas accumlated 30,000 lines of code and features to do with all things Haskell related.haskell-ts-modejust keeps the scope to basic major mode stuff, and leaves other stuff to external packages.
haskell-mode contains nearly 30k lines of code, and is about 30 years
old. A lot of features implemented by haskell-mode are now also
available in standard Emacs, and have thus become obsolete.
In 2018, a mode called =haskell-tng-mode= was made to solve some of these problems. However, because of Haskell’s syntax, it too became very complex and required a web of dependencies.
Both these modes ended up practically parsing Haskell’s syntax to implement indentation, so I thought why not use Tree-sitter?
This mode provides strucural navigation, for Emacs 30+.combs (x:xs) = map (x:) c ++ c
where c = combs xsIn the above code, if the pointer is right in front of the function
definition combs, and you press C-M-f (forward-sexp), it will take
you to the end of the second line.
(use-package haskell-ts-mode
:ensure t
:custom
(haskell-ts-font-lock-level 4)
(haskell-ts-use-indent t)
(haskell-ts-ghci "ghci")
(haskell-ts-use-indent t)
:config
(add-to-list 'treesit-language-source-alist
'(haskell . ("https://github.com/tree-sitter/tree-sitter-haskell" "v0.23.1")))
(unless (treesit-grammar-location 'haskell)
(treesit-install-language-grammar 'haskell)))That is all. This will install the grammars if not already installed. However, you might need to update the grammar version in the future.
Unlike haskell-mode, this mode has a limited scope to just worrying
about haskell. There are other packages that I find help a lot with
development:
- consult-hoogle great interface for
hoogle. - dante
- apheleia I suggest using this with ormolu to provide formatting for haskell.
- hcel Codebase navigator, if you want a lighter alternaitve to a full blown LSP.
Indentation has been disabled by default. To enable it, use the following code.
(setq haskell-ts-use-indent t)Simply because the indention code became a monstrosity. Don’t belive
me? check the haskell-ts-indent-rules variable. Bugs are rampent,
fixing one bug created another. Its a torturous game of wack-a-mole
with no end in sight.
Check out the newindent branch to see the repo to see the progress.
There are some options to rewriting indentation:
- Do the same approach of having strict indentation that doesn’t modify the syntax tree, just impliment it better, potentilly using a style guide, like this one.
- Rely on a external package like hi2 or hyai, at the compromise they don’t use treesitter, so just end up inefficialty reparsing haskell.
- My prefered: try to impliment haskell-mode type indentation. I have no idea how we would do this, since each indentation attempt could change the parse tree, changing the treesitter concrete syntax tree.
- Adaptive indentation: Like
python-mode, we can try memorise the user’s indentation prefrences.
prettify-symbols-mode can be used to replace common symbols with
unicode alternatives.
Turning on prettify-symbols-mode does stuff like turn -> to →. If
you want to prettify words, set haskell-ts-prettify-words to non-nil.
This will do stuff like prettify forall into ∀ and elem to ∈.
(add-hook 'haskell-ts-mode 'prettify-symbols-mode)Set haskell-ts-font-lock-level accordingly. The default and highest
value is 4. You are against vibrancy, you can lower it to match your
dreariness.
haskell-ts-mode works with lsp-mode and, since Emacs 30, with eglot.
To add eglot support on Emacs 29 and earlier, add the following code
to your init.el:
(with-eval-after-load 'eglot
(defvar eglot-server-programs)
(add-to-list 'eglot-server-programs
'(haskell-ts-mode . ("haskell-language-server-wrapper" "--lsp"))))
- Support for M-x align, so that calling it will align all the ‘=’ signs in a region.
- Imenu support for functions with multiple definitions.
- Merge the indent branch
