spacemacs/layers/+lang/haskell/README.org

15 KiB
Raw Blame History

Haskell contribution layer for Spacemacs

/TakeV/spacemacs/media/commit/13c5b1d24b7dab52d78aa236786cc6d4f3724d0a/layers/+lang/haskell/img/haskell.png

Description

This layer adds support for the Haskell language.

Features:

This layer is in construction, it needs your contributions and bug reports.

Install

Layer

To use this contribution add it to your ~/.spacemacs

(setq-default dotspacemacs-configuration-layers '(haskell))

Dependencies

This layer requires some cabal packages:

  • hlint
  • stylish-haskell
  • hasktags
  • ghc-mod

To install them, use following command (or the stack equivalent):

$ cabal install stylish-haskell hlint hasktags ghc-mod

Setup PATH

First of all make sure that your $PATH contains the installation path for Haskell tools like ghc, ghci etc. It depends on how you have installed ghc, but you can always check it by running which ghc in your terminal. Stack users should add only the installation path of stack itself. Usually it's ~/.local/bin.

Then make sure that your $PATH contains the installation path for cabal packages. If you are using cabal it should be ~/.cabal/bin or ~/Library/Haskell/bin (for 'Haskell for Mac' users). If you are using stack then it should be ~/.local/bin.

For information about setting up $PATH, check out the corresponding section in the FAQ (SPC f e h $PATH RET).

ghc-mod support

ghc-mod enhances haskell-mode with for example code completion, templates, case-splitting and much more. In order to use it you need to install the executable with cabal install ghc-mod (or stack equivalent). Note that ghc-mod support is enabled by default, so if you want to disable it, you must do it explicitly in your .spacemacs file.

(setq-default dotspacemacs-configuration-layers
  '((haskell :variables haskell-enable-ghc-mod-support nil)))

Stack users also should make sure that dist/setup-config doesn't exist in the project root. As it will confuse ghc-mod. For more troubleshooting, checkout this document.

Stack users

Time to time ghc-mod is not available via latest Stackage LTS version. So if you have problems with calling stack install ghc-mod, try to use stack install ghc-mod --resolver lts-3.1 (the last known LTS version that had ghc-mod). But even if it doesn't work, don't panic, it's easy to install it from sources.

# clone cabal-helper
$ git clone https://github.com/DanielG/cabal-helper.git
# cd into cloned repository
$ cd cabal-helper
# init stack, so cabal-helper can be installed using stack
$ stack init
# now install it
$ stack install
# go back where you was
$ cd ..
# and remove cabal-helper repository since you don't need it
$ rm -rf cabal-helper
# now clone ghc-mod
$ git clone https://github.com/kazu-yamamoto/ghc-mod.git
# and cd into it
$ cd ghc-mod
# again, let's init stack
$ stack init
# now install it
$ stack install
# go back
$ cd ...
# and remove ghc-mod since you don't need it
$ rm -rf ghc-mod

Optional extras

The Haskell layer supports some extra features that can be enabled through layer variables.

GHCi-ng support

ghci-ng adds some nice features to haskell-mode, and is supported in Spacemacs by a layer variable:

Follow the instructions to install ghci-ng (remember to add :set +c in ~/.ghci), next set the layer variable:

  (setq-default dotspacemacs-configuration-layers
    '((haskell :variables haskell-enable-ghci-ng-support t)))

Once ghci-ng is enabled, two of the old keybindings are overriden with improved versions from ghci-ng, and a new keybinding available:

Key Binding Description
SPC m h t gets the type of the identifier under the cursor or for the active region
SPC m g g go to definition
SPC m u finds uses of identifier
Stack users

Stack and ghci-ng doesn't play well with each other, so the general advice is to avoid mixing them. But, if you want mix them anyway, you'll need to set haskell-process-type explicitly:

(setq-default dotspacemacs-configuration-layers
  '((haskell :variables haskell-process-type 'ghci)))
;; or
(setq-default dotspacemacs-configuration-layers
  '((haskell :variables haskell-process-type cabal-repl)))

This is needed, because by default haskell-process-type is set to auto and if you have stack.yaml file in the root of your project, stack-ghci will be used as process type.

ghc-mod users

If you want to use SPC m h t from ghc-mod instead of ghci-ng - then you need to add following line in your dotspacemacs/user-config:

(evil-leader/set-key-for-mode 'haskell-mode
        "mht"  'ghc-show-type)

This might be useful, because ghc-mod doesn't require active REPL in order to get type of symbol.

structured-haskell-mode

structured-haskell-mode, or shm, replaces default haskell-mode auto-indentation and adds some nice functionalities.

To install shm run cabal install structured-haskell-mode (or stack equivalent).

To enable shm set the layer variable:

  (setq-default dotspacemacs-configuration-layers
    '((haskell :variables haskell-enable-shm-support t)))

After shm has been enabled, some of the evil normal state bindings are overridden:

Key Binding Description
D shm/kill-line
R shm/raise
P shm/yank
( shm/forward-node
) shm/backward-node

For a nice visualization of these functions, please refer to the github page for structured-haskell-mode.

Warning structured-haskell-mode doesn't play very well with evil (structured-haskell-mode/#81). So it's better to be used with emacs edit style.

hindent

hindent is an extensible Haskell pretty printer, which let's you reformat your code. You need to install the executable with cabal install hindent or stack install hindent

To enable it you have to set the variable haskell-enable-hindent-style to a supported style. The available styles are:

  • fundamental
  • johan-tibell
  • chris-done
  • gibiansky

See examples here

(setq-default dotspacemacs-configuration-layers
  '((haskell :variables haskell-enable-hindent-style "johan-tibell")))

Key bindings

All Haskell specific bindings are prefixed with the major-mode leader SPC m.

Top-level commands are prefixed by SPC m:

Key Binding Description
SPC m g g go to definition or tag
SPC m g i cycle the Haskell import lines or return to point (with prefix arg)
SPC m f format buffer using haskell-stylish
SPC m F format declaration using hindent (if enabled)

Documentation

Documentation commands are prefixed by SPC m h

Key Binding Description
SPC m h d find or generate Haddock documentation for the identifier under the cursor
SPC m h h do a Hoogle lookup
SPC m h H do a local Hoogle lookup
SPC m h i gets information for the identifier under the cursor
SPC m h t gets the type of the identifier under the cursor
SPC m h y do a Hayoo lookup

Debug

Debug commands are prefixed by SPC m d:

Key Binding Description
SPC m d d start debug process, needs to be run first
SPC m d b insert breakpoint at function
SPC m d n next breakpoint
SPC m d N previous breakpoint
SPC m d B delete breakpoint
SPC m d c continue current process
SPC m d a abandon current process
SPC m d r refresh process buffer

REPL

REPL commands are prefixed by SPC m s:

Key Binding Description
SPC m s b load or reload the current buffer into the REPL
SPC m s c clear the REPL
SPC m s s show the REPL without switching to it
SPC m s S show and switch to the REPL

Cabal commands

Cabal commands are prefixed by SPC m c:

Key Binding Description
SPC m c a cabal actions
SPC m c b build the current cabal project, i.e. invoke cabal build
SPC m c c compile the current project, i.e. invoke ghc
SPC m c v visit the cabal file

Cabal files

This commands are available in a cabal file.

Key Binding Description
SPC m d add a dependency to the project
SPC m b go to benchmark section
SPC m e go to executable section
SPC m t go to test-suite section
SPC m m go to exposed modules
SPC m l go to libary section
SPC m n go to next subsection
SPC m p go to previous subsection
SPC m s c clear the REPL
SPC m s s show the REPL without switching to it
SPC m s S show and switch to the REPL
SPC m N go to next section
SPC m P go to previous section
SPC m f find or create source-file under the cursor

FAQ

REPL doesn't work

Usually haskell-mode is great at figuring out which interactive process to bring up. But if you are experiencing problems with it you can help haskell-mode by setting haskell-process-type as in following code:

(setq-default dotspacemacs-configuration-layers
  '((haskell :variables haskell-process-type 'stack-ghci)))

Available options are:

  • ghci
  • cabal-repl
  • cabal-dev
  • cabal-ghci
  • stack-ghci

REPL is stuck

Make sure that when you are typing anything in REPL there is a space between what you type and λ>. When there is no space - REPL will behave as it's stuck. Usually, when you enter normal state, cursor is moved back, so there is no required space when you switch to insert mode. There is possible workaround - just add following snippet to your dotspacemacs/user-config function:

(when (configuration-layer/layer-usedp 'haskell)
  (add-hook 'haskell-interactive-mode-hook
            (lambda ()
              (setq-local evil-move-cursor-back nil))))

It will make cursor stay at the right place in the REPL buffer when you enter normal state. Which in most cases helps you to avoid the problem with 'stuck' REPL.

Also, some users might want to start REPL in insert mode. For this to happen you could place following snippet in your dotspacemacs/user-config function:

(when (configuration-layer/layer-usedp 'haskell)
    (defadvice haskell-interactive-switch (after spacemacs/haskell-interactive-switch-advice activate)
      (when (eq dotspacemacs-editing-style 'vim)
        (call-interactively 'evil-insert))))

I am using stack and ghc-mod, but ghc-mod doesn't work

Make sure that dist directory doesn't exist in your project root. So if it exists, just remove it and try again.

Indentation doesn't reset when pressing return after empty line

This is intended behavior in haskell-indentation-mode. If you want to reset indentation when pressing return after empty line, add following snippet into your dotspacemacs/user-config function.

(defun haskell-indentation-advice ()
  (when (and (< 1 (line-number-at-pos))
             (save-excursion
               (forward-line -1)
               (string= "" (s-trim (buffer-substring (line-beginning-position) (line-end-position))))))
    (delete-region (line-beginning-position) (point))))

(advice-add 'haskell-indentation-newline-and-indent
            :after 'haskell-indentation-advice)