merge: pull request #5337 from iyefrat/haskell

Improve `:lang haskell`
This commit is contained in:
Henrik Lissner 2021-09-15 00:58:52 +02:00 committed by GitHub
commit 7265fecd19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 192 deletions

View file

@ -1,41 +0,0 @@
;;; lang/haskell/+dante.el -*- lexical-binding: t; -*-
;;;###if (featurep! +dante)
(use-package! dante
:hook (haskell-mode-local-vars . dante-mode)
:init
(setq dante-load-flags '(;; defaults:
"+c"
"-Wwarn=missing-home-modules"
"-fno-diagnostics-show-caret"
;; necessary to make attrap-attrap useful:
"-Wall"
;; necessary to make company completion useful:
"-fdefer-typed-holes"
"-fdefer-type-errors"))
:config
(when (featurep! :checkers syntax)
(flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint)))
(set-company-backend! 'dante-mode #'dante-company)
(defadvice! +haskell--restore-modified-state-a (fn &rest args)
"Marks the buffer as falsely modified.
Dante quietly saves the current buffer (without triggering save hooks) before
invoking flycheck, unexpectedly leaving the buffer in an unmodified state. This
is annoying if we depend on save hooks to do work on the buffer (like
reformatting)."
:around #'dante-async-load-current-buffer
(let ((modified-p (buffer-modified-p)))
(apply fn args)
(if modified-p (set-buffer-modified-p t))))
(when (featurep 'evil)
(add-hook 'dante-mode-hook #'evil-normalize-keymaps))
(map! :map dante-mode-map
:localleader
"t" #'dante-type-at
"i" #'dante-info
"l" #'haskell-process-load-file
"e" #'dante-eval-block
"a" #'attrap-attrap))

View file

@ -1,11 +0,0 @@
;;; lang/haskell/+lsp.el -*- lexical-binding: t; -*-
(use-package! lsp-haskell
:after lsp-mode
:preface (add-hook 'haskell-mode-local-vars-hook #'lsp!)
:config
(when (featurep! +ghcide)
(setq lsp-haskell-server-path "ghcide"
lsp-haskell-server-args nil))
;; Does some strange indentation if it pastes in the snippet
(setq-hook! 'haskell-mode-hook yas-indent-line 'fixed))

View file

@ -5,152 +5,69 @@
* Table of Contents :TOC:
- [[#description][Description]]
- [[#external-resources][External resources]]
- [[#maintainers][Maintainers]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#cabal][Cabal]]
- [[#lsp-haskell-language-server][LSP (haskell-language-server)]]
- [[#lsp-ghcide][LSP (ghcide)]]
- [[#stack][Stack]]
- [[#haskell-packages][Haskell packages]]
- [[#features][Features]]
- [[#configuration][Configuration]]
- [[#using-the-new-style-cabal-repl][Using the new-style cabal REPL]]
- [[#troubleshooting][Troubleshooting]]
* Description
This module adds [[https://www.haskell.org/][Haskell]] support, powered by either [[https://github.com/jyp/dante][dante]] (the default) or LSP
(haskell-language-server or ghcide).
Adds Haskell support to Doom Emacs.
+ Code completion (~company-ghc~)
+ Look up documentation (~hoogle~)
+ eldoc support (~dante~)
+ REPL (~ghci~)
+ Syntax-checking (~flycheck~)
+ Code navigation (~dante~)
+ [[https://github.com/hlissner/doom-snippets/tree/master/haskell-mode][Snippets]]
** Maintainers
This module has no dedicated maintainers.
** External resources
Here are a few resources I've found indispensable in my Haskell adventures:
+ [[http://learnyouahaskell.com/][Learn you a haskell for great good]]
+ [[http://haskellbook.com/][Haskell Programming from first principles]]
+ [[https://github.com/krispo/awesome-haskell][Awesome Haskell]]: an extensive list of haskell resources
+ [[https://docs.haskellstack.org/en/stable/README/][The Haskell Tool Stack docs]]
** Module Flags
+ =+dante= Enables dante; a fork of intero aimed at lightweightedness. It
doesn't depend on =stack=, supports both ~cabal~-only and ~stack~ projects,
but lacks eldoc support.
+ =+ghcide= Enables LSP support with ghcide (requires the ~:tools lsp~ module).
+ =+lsp= Enables LSP support with haskell-language-server (requires the ~:tools lsp~
module).
+ =+lsp= Enable LSP support with for [[https://github.com/haskell/haskell-language-server][haskell-language-server]] (requires the =:tools lsp= module).
** Plugins
+ [[https://github.com/haskell/haskell-mode][haskell-mode]]
+ =+dante=
+ [[https://github.com/jyp/dante][dante]]
+ [[https://github.com/jyp/attrap][attrap]]
+ =+lsp=
+ [[https://github.com/emacs-lsp/lsp-haskell][lsp-haskell]]
+ [[https://github.com/emacs-lsp/lsp-haskell][lsp-haskell]] (=+lsp=, =:tools lsp=)
* Prerequisites
Depending on whether you use Dante, haskell-language-server or ghcide, your
dependencies will differ:
It is recommended to install the haskell tooling using [[https://www.haskell.org/ghcup/][ghcup]]. Only ghc is needed
for basic functionality:
+ Dante users need =cabal=, =ghc= and =ghc-mod=
+ LSP users need the =haskell-language-server= LSP server OR =ghcide=
+ All users will need the =hoogle= package
** Cabal
To use Dante, you need =cabal= (the haskell package builder) and =ghci= (the
compiler, syntax checker & repl):
*** MacOS
#+BEGIN_SRC sh
brew install cabal-install ghc
#+BEGIN_SRC bash
ghcup install ghc
#+END_SRC
*** Arch Linux
#+BEGIN_SRC sh
sudo pacman -S cabal-install ghc
#+END_SRC
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install cabal-install ghc
#+END_SRC
** LSP (haskell-language-server)
You will need =stack= and =git= installed.
You will find a comprehensive [[https://github.com/haskell/haskell-language-server#installation][instructions for haskell-language-server on its project page]], but if you are using [[https://www.haskell.org/ghcup/][ghcup]]:
but =+lsp= users should also install the language server:
#+BEGIN_SRC bash
ghcup install hls
#+END_SRC
** LSP (ghcide)
See https://github.com/digital-asset/ghcide for install instructions.
Installing [[https://www.haskell.org/cabal/][cabal]] or [[https://docs.haskellstack.org/en/stable/README/][stack]] as well is recommended, and can be done through
=ghcup=.
** Stack
To use LSP, you need =stack=:
=haskell-mode= provides support for [[https://github.com/ndmitchell/hoogle][hoogle]], which can be installed through
system package manager, cabal, or stack.
*** MacOS
#+BEGIN_SRC sh
brew install haskell-stack
stack setup
#+END_SRC
*** Arch Linux
#+BEGIN_SRC sh
sudo pacman -S stack
# Replace pacaur with your AUR package manager of choice
pacaur -S ncurses5-compat-lib
stack setup
#+END_SRC
=haskell-language-server= provides support for [[https://github.com/ndmitchell/hlint/][hlint]], and haskell code
formatters such as [[https://github.com/lspitzner/brittany][brittany]], [[https://github.com/ennocramer/floskell][floskell]], [[https://github.com/tweag/ormolu][ormolu]], [[https://github.com/fourmolu/fourmolu][fourmolu]], and [[https://github.com/haskell/stylish-haskell][stylish-haskell]],
which can be installed through system package manager, cabal, or stack.
*** openSUSE
#+BEGIN_SRC sh :dir /sudo::
sudo zypper install stack
stack setup
#+END_SRC
* Features
This module intergrates the haskell packages into Doom by providing things such
as repl support, project root recognition, etc. It also provide the following
keybindings:
** Haskell packages
You'll need to install the following packages using ~stack~ or ~cabal~:
+ (Dante users) =ghc-mod=
#+BEGIN_SRC sh
stack install ghc-mod
# or
cabal install ghc-mod
#+END_SRC
+ =hoogle=
#+BEGIN_SRC sh
cabal update
cabal install happy haskell-src-exts # ghc-mod/hoogle dependencies
cabal ghc-mod hoogle
# or
stack install ghc-mod
stack install hoogle
#+END_SRC
And ensure the binaries for these packages are in your ~PATH~, e.g.
#+BEGIN_SRC sh
# place this in your profile file, like ~/.bash_profile or ~/.zshenv
export PATH="~/.local/bin:$PATH"
#+END_SRC
| Keybinding | Description |
|-------------------+-----------------------------------------------|
| =<localleader> b= | Build the current cabal project |
| =<localleader> c= | Visit the =.cabal= file of the current buffer |
| =<localleader> h= | Toggle visibility of the form at point |
| =<localleader> H= | hides all top level functions |
* Configuration
** Using the new-style cabal REPL
=haskell-mode= will typically detect what REPL to run based on your project
(e.g. stack, (old-style) cabal or ghc). If you want the new-style cabal REPL you
must set ~haskell-process-type~ manually:
After installing your preferred formatter, make sure to set
=lsp-haskell-formatting-provider= to it.
#+BEGIN_SRC emacs-lisp
(setq haskell-process-type 'cabal-new-repl)
Make sure to configure the lsp to use your perfered formatter, e.g.:
#+BEGIN_SRC elisp
;; ~/.doom.d/config.el
(after!
(setq lsp-haskell-formatting-provider "brittany"))
#+END_SRC
* Troubleshooting
+ Stack users: a ~dist/setup-config~ file in your project may cause [[https://github.com/DanielG/ghc-mod/wiki#known-issues-related-to-stack][ghc-mod to
not work]].

View file

@ -43,10 +43,10 @@
"H" #'haskell-hide-toggle-all))
;;
;;; Backends
(cond ((featurep! +dante) (load! "+dante"))
((or (featurep! +lsp)
(featurep! +ghcide))
(load! "+lsp")))
(use-package! lsp-haskell
:when (featurep! +lsp)
:after lsp-mode
:preface (add-hook 'haskell-mode-local-vars-hook #'lsp!)
:config
;; Does some strange indentation if it pastes in the snippet
(setq-hook! 'haskell-mode-hook yas-indent-line 'fixed))

View file

@ -5,8 +5,22 @@
(featurep! :tools lsp))
"This module requires (:tools lsp)")
(when (featurep! +dante)
(unless (executable-find "cabal")
(warn! "Couldn't find cabal, haskell-mode may have issues"))
(unless (executable-find "hlint")
(warn! "Couldn't find hlint. Flycheck may have issues in haskell-mode")))
(unless (executable-find "cabal")
(warn! "Couldn't find cabal. haskell-mode may have issues."))
(unless (executable-find "hoogle")
(warn! "Couldn't find hoogle. Documentation searching will not work."))
(unless (or (featurep! +lsp)
(executable-find "hlint"))
(warn! "Couldn't find hlint. Flycheck may have issues in haskell-mode.
Install it or enable +lsp."))
(when (and (featurep! :editor format)
(not (executable-find "brittany")))
(warn! "Couldn't find brittany. Code formatting will not work.
Install it or enable +lsp."))
(when (and (featurep! +lsp)
(not (executable-find "haskell-language-server-wrapper")))
(warn! "Couldn't find haskell-language-server."))

View file

@ -3,10 +3,6 @@
(package! haskell-mode :pin "98ba3922360199d5260d47f417f096730ad057c5")
(when (featurep! +dante)
(package! dante :pin "8741419333fb85ed2c1d71f5902688f5201b0a40")
(package! attrap :pin "a5bc695af27349ae6fe4541a581e6fd449d2a026"))
(when (or (and (featurep! +lsp)
(not (featurep! :tools lsp +eglot)))
(featurep! +ghcide))
(when (and (featurep! +lsp)
(not (featurep! :tools lsp +eglot)))
(package! lsp-haskell :pin "4e62cf897dd9e9fcef25c6e8e483490a07a5d439"))