From a8e569d3b20a32d28a1dbaf1cc3b729b5747aa37 Mon Sep 17 00:00:00 2001 From: d12frosted Date: Fri, 10 Jun 2016 19:53:07 +0300 Subject: [PATCH] refine haskell completion backends - add haskell-completion-backend variable that should be used to select desired completion backend - add support for intero (based on @cydparser layer) - remove ghci-ng support - update readme file: - document haskell-completion-backend variable - remove installation notes for ghc-mod as they are not relevant anymore - remove ghci-ng section - overall readme file fixes and improvements --- layers/+lang/haskell/README.org | 162 +++++++++------------------- layers/+lang/haskell/config.el | 10 +- layers/+lang/haskell/packages.el | 177 ++++++++++++++++++------------- 3 files changed, 164 insertions(+), 185 deletions(-) diff --git a/layers/+lang/haskell/README.org b/layers/+lang/haskell/README.org index e1c6057a0..cce90e1a7 100644 --- a/layers/+lang/haskell/README.org +++ b/layers/+lang/haskell/README.org @@ -9,12 +9,11 @@ - [[#layer][Layer]] - [[#dependencies][Dependencies]] - [[#setup-path][Setup PATH]] - - [[#ghc-mod-support][ghc-mod support]] - - [[#stack-users][Stack users]] + - [[#completion-support][Completion support]] + - [[#company-ghci][=company-ghci=]] + - [[#intero][=intero=]] + - [[#ghc-mod][=ghc-mod=]] - [[#optional-extras][Optional extras]] - - [[#ghci-ng-support][GHCi-ng support]] - - [[#stack-users][Stack users]] - - [[#ghc-mod-users][ghc-mod users]] - [[#structured-haskell-mode][structured-haskell-mode]] - [[#hindent][hindent]] - [[#key-bindings][Key bindings]] @@ -22,15 +21,16 @@ - [[#debug][Debug]] - [[#debug-buffer][Debug Buffer]] - [[#repl][REPL]] + - [[#intero-repl][Intero REPL]] - [[#cabal-commands][Cabal commands]] - [[#cabal-files][Cabal files]] - [[#refactor][Refactor]] - - [[#ghc-mod][Ghc-mod]] + - [[#ghc-mod-1][Ghc-mod]] - [[#insert-template][Insert template]] - [[#syntax-checking][Syntax checking]] - [[#flycheck][Flycheck]] - [[#hlint][HLint]] - - [[#ghc-mod][ghc-mod]] + - [[#ghc-mod-2][ghc-mod]] - [[#interactive-haskell-mode][Interactive haskell-mode]] - [[#flymake][Flymake]] - [[#troublesshooting][Troublesshooting]] @@ -55,7 +55,7 @@ This layer adds support for the [[https://www.haskell.org/][Haskell]] language. ** Features: - syntax highlighting for [[https://github.com/haskell/haskell-mode][haskell source]], [[https://github.com/haskell/haskell-mode][cabal files]], [[https://github.com/bgamari/cmm-mode][C-- source]], -- auto-completion with [[https://github.com/iquiw/company-ghc][company-ghc]]. +- auto-completion with one of selected backends (=intero=, =ghci= or =ghc-mod=). *This layer is in construction, it needs your contributions and bug reports.* @@ -67,17 +67,18 @@ file. ** Dependencies This layer requires some [[https://www.haskell.org/cabal/][cabal]] packages: -- =apply-refact= -- =hlint= -- =stylish-haskell= -- =hasktags= -- =ghc-mod= -- =hoogle= +- =apply-refact= (required by =hlint-refactor=) +- =hlint= (required by =hlint-refactor=) +- =stylish-haskell= (optional for =haskell-mode=) +- =hasktags= (optional) +- =hoogle= (optional for =haskell-mode= and =helm-hoogle=) +- =ghc-mod= (optional for completion) +- =intero= (optional for completion) To install them, use following command (or the =stack= equivalent): #+BEGIN_SRC sh -$ cabal install apply-refact stylish-haskell hlint hasktags ghc-mod hoogle +$ cabal install apply-refact hlint stylish-haskell hasktags hoogle #+END_SRC ** Setup PATH @@ -95,110 +96,42 @@ are using =stack= then it should be =~/.local/bin=. For information about setting up =$PATH=, check out the corresponding section in the FAQ (~SPC h SPC $PATH RET~). -** ghc-mod support -[[http://www.mew.org/~kazu/proj/ghc-mod/][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. +** Completion support +This layer provides several completion backends - =intero=, =ghci= and =ghc-mod=. By +default =ghci= (=company-ghci=) is used as it requires no dependencies and works +both with =stack= and pure =cabal= projects. In order to manually set completion +backend set value of =haskell-completion-backend=. #+BEGIN_SRC emacs-lisp (setq-default dotspacemacs-configuration-layers - '((haskell :variables haskell-enable-ghc-mod-support nil))) + '((haskell :variables haskell-completion-backend 'intero))) #+END_SRC -=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 [[https://github.com/kazu-yamamoto/ghc-mod/wiki#known-issues-related-to-stack][document]]. +*** =company-ghci= +[[https://github.com/juiko/company-ghci][company-ghci]] communicates directly with =ghci= in order to provide completion. In +order to use it you have to call =haskell-process-load-or-reload= (=SPC s b=). -*** Stack users -From 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. +*** =intero= +=Intero= works only for =stack= users. You can manually install =intero= executable by +calling =stack install intero=, but this step is optional as =Intero= installs +itself. -#+BEGIN_SRC sh -# 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 -#+END_SRC +*** =ghc-mod= +[[http://www.mew.org/~kazu/proj/ghc-mod/][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). + +=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 [[https://github.com/kazu-yamamoto/ghc-mod/wiki#known-issues-related-to-stack][document]]. + +Also note that =ghc-mod= works only with =GHC= version that was used to build +=ghc-mod=. You can check which version was used by calling =ghc-mod --version=. ** Optional extras The Haskell layer supports some extra features that can be enabled through layer variables. -*** GHCi-ng support -[[https://github.com/chrisdone/ghci-ng][ghci-ng]] adds some nice features to =haskell-mode=, and is supported in Spacemacs -by a layer variable: - -Follow the instructions to install [[https://github.com/chrisdone/ghci-ng][ghci-ng]] (remember to add =:set +c= in -=~/.ghci=), next set the layer variable: - -#+BEGIN_SRC emacs-lisp - (setq-default dotspacemacs-configuration-layers - '((haskell :variables haskell-enable-ghci-ng-support t))) -#+END_SRC - -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: - -#+BEGIN_SRC emacs-lisp -(setq-default dotspacemacs-configuration-layers - '((haskell :variables haskell-process-type 'ghci))) -;; or -(setq-default dotspacemacs-configuration-layers - '((haskell :variables haskell-process-type cabal-repl))) -#+END_SRC - -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=: - -#+BEGIN_SRC emacs-lisp -(spacemacs/set-leader-keys-for-major-mode 'haskell-mode - "ht" 'ghc-show-type) -#+END_SRC - -This might be useful, because =ghc-mod= doesn't require active REPL in order to -get type of symbol. - *** structured-haskell-mode Currently there is no support for [[https://github.com/chrisdone/structured-haskell-mode][structured-haskell-mode]], since it doesn't play very well with non-emacs editing style ([[https://github.com/chrisdone/structured-haskell-mode/issues/81][structured-haskell-mode/#81]]). Emacs @@ -298,8 +231,19 @@ REPL commands are prefixed by ~SPC m s~: | ~SPC m s s~ | show the REPL without switching to it | | ~SPC m s S~ | show and switch to the REPL | -** Cabal commands +** Intero REPL +Intero REPL commands are prefixed by ~SPC m i~: +| Key Binding | Description | +|-------------+-----------------------------------------------------------| +| ~SPC m i c~ | change directory in the backend process | +| ~SPC m i d~ | reload the module =DevelMain= and then run =DevelMain.update= | +| ~SPC m i k~ | stop the current worker process and kill its associated | +| ~SPC m i l~ | list hidden process buffers created by =intero= | +| ~SPC m i r~ | restart the process with the same configuration as before | +| ~SPC m i t~ | set the targets to use for stack =ghci= | + +** Cabal commands Cabal commands are prefixed by ~SPC m c~: | Key Binding | Description | @@ -349,7 +293,7 @@ ghc-mod commands are prefixed by ~SPC m m~: | Key Binding | Description | |-------------+-----------------------------------------| -| ~SPC m m t~ | insert template | +| ~SPC t~ | insert template | | ~SPC m m u~ | insert template with holes | | ~SPC m m a~ | select one of possible cases (~ghc-auto~) | | ~SPC m m f~ | replace a hole (~ghc-refine~) | diff --git a/layers/+lang/haskell/config.el b/layers/+lang/haskell/config.el index e41ab8f5e..8e022a28d 100644 --- a/layers/+lang/haskell/config.el +++ b/layers/+lang/haskell/config.el @@ -15,12 +15,12 @@ (spacemacs|defvar-company-backends haskell-mode) (spacemacs|defvar-company-backends haskell-cabal-mode) +(spacemacs|defvar-company-backends intero-repl-mode) -(defvar haskell-enable-ghci-ng-support nil - "If non-nil ghci-ng support is enabled") +(defvar haskell-completion-backend 'ghci + "Completion backend used by company. +Available options are `ghci', `intero' and `ghc-mod'. Default is +`ghci'.") (defvar haskell-enable-hindent-style nil "Style to use for formatting with hindent; available are: fundamental johan-tibell chris-done gibiansky. If nil hindent is disabled.") - -(defvar haskell-enable-ghc-mod-support t - "If non-nil ghc-mod support is enabled") diff --git a/layers/+lang/haskell/packages.el b/layers/+lang/haskell/packages.el index e9819fb52..0398140d8 100644 --- a/layers/+lang/haskell/packages.el +++ b/layers/+lang/haskell/packages.el @@ -14,13 +14,21 @@ cmm-mode company (company-cabal :toggle (configuration-layer/package-usedp 'company)) - (company-ghc :toggle (and (configuration-layer/package-usedp 'company) - haskell-enable-ghc-mod-support)) + + ;; ghci completion backend (company-ghci :toggle (and (configuration-layer/package-usedp 'company) - (not haskell-enable-ghc-mod-support))) + (eq haskell-completion-backend 'ghci))) + + ;; ghc-mod completion backend + (company-ghc :toggle (and (configuration-layer/package-usedp 'company) + (eq haskell-completion-backend 'ghc-mod))) + (ghc :toggle (eq haskell-completion-backend 'ghc-mod)) + + ;; intero completion backend + (intero :toggle (eq haskell-completion-backend 'intero)) + flycheck (flycheck-haskell :toggle (configuration-layer/package-usedp 'flycheck)) - ghc haskell-mode haskell-snippets (helm-hoogle :toggle (configuration-layer/package-usedp 'helm)) @@ -34,19 +42,9 @@ (defun haskell/post-init-company () (spacemacs|add-company-hook haskell-mode) - (spacemacs|add-company-hook haskell-cabal-mode)) - -(defun haskell/init-company-ghc () - (use-package company-ghc - :defer t - :init (push '(company-ghc company-dabbrev-code company-yasnippet) - company-backends-haskell-mode))) - -(defun haskell/init-company-ghci () - (use-package company-ghc - :defer t - :init (push '(company-ghci company-dabbrev-code company-yasnippet) - company-backends-haskell-mode))) + (spacemacs|add-company-hook haskell-cabal-mode) + (when (eq haskell-completion-backend 'intero) + (spacemacs|add-company-hook intero-repl-mode))) (defun haskell/init-company-cabal () (use-package company-cabal @@ -56,25 +54,21 @@ (push '(company-cabal) company-backends-haskell-cabal-mode))) -(defun haskell/init-helm-hoogle () - (use-package helm-hoogle +(defun haskell/init-company-ghci () + (use-package company-ghci :defer t - :init - (dolist (mode haskell-modes) - (spacemacs/set-leader-keys-for-major-mode mode "hf" 'helm-hoogle)))) + :init (push '(company-ghci company-dabbrev-code company-yasnippet) + company-backends-haskell-mode))) -(defun haskell/post-init-flycheck () - (spacemacs/add-flycheck-hook 'haskell-mode)) - -(defun haskell/init-flycheck-haskell () - (use-package flycheck-haskell - :commands flycheck-haskell-configure - :init (add-hook 'flycheck-mode-hook 'flycheck-haskell-configure))) +(defun haskell/init-company-ghc () + (use-package company-ghc + :defer t + :init (push '(company-ghc company-dabbrev-code company-yasnippet) + company-backends-haskell-mode))) (defun haskell/init-ghc () (use-package ghc - :defer t - :if haskell-enable-ghc-mod-support + :after (haskell-mode) :init (add-hook 'haskell-mode-hook 'ghc-init) :config (progn @@ -97,6 +91,82 @@ (set-face-attribute 'ghc-face-error nil :underline nil) (set-face-attribute 'ghc-face-warn nil :underline nil))))) +(defun haskell/init-intero () + (use-package intero + :after (haskell-mode) + :init + (progn + (push '(company-intero company-dabbrev-code company-yasnippet) + company-backends-haskell-mode) + (add-hook 'haskell-mode-hook #'intero-mode)) + :config + (progn + (spacemacs|diminish intero-mode " λ" " \\") + + (defun haskell-intero/insert-type () + (interactive) + (intero-type-at :insert)) + + (defun haskell-intero/display-repl () + (interactive) + (let ((buffer (intero-repl-buffer))) + (unless (get-buffer-window buffer 'visible) + (display-buffer (intero-repl-buffer))))) + + (defun haskell-intero/pop-to-repl () + (interactive) + (pop-to-buffer (intero-repl-buffer))) + + (defun haskell-intero//preserve-focus (f) + (let ((buffer (current-buffer))) + (funcall f) + (pop-to-buffer buffer))) + + (advice-add 'intero-repl-load + :around #'haskell-intero//preserve-focus) + + (dolist (mode haskell-modes) + (spacemacs/set-leader-keys-for-major-mode mode + "gg" 'intero-goto-definition + "hi" 'intero-info + "ht" 'intero-type-at + "hT" 'haskell-intero/insert-type + "sb" 'intero-repl-load)) + + (dolist (mode (cons 'haskell-cabal-mode haskell-modes)) + (spacemacs/set-leader-keys-for-major-mode mode + "sc" nil + "ss" 'haskell-intero/display-repl + "sS" 'haskell-intero/pop-to-repl)) + + (dolist (mode (append haskell-modes '(haskell-cabal-mode intero-repl-mode))) + (spacemacs/declare-prefix-for-mode mode "mi" "haskell/intero") + (spacemacs/set-leader-keys-for-major-mode mode + "ic" 'intero-cd + "id" 'intero-devel-reload + "ik" 'intero-destroy + "il" 'intero-list-buffers + "ir" 'intero-restart + "it" 'intero-targets)) + + (evil-define-key '(insert normal) intero-mode-map + (kbd "M-.") 'intero-goto-definition)))) + +(defun haskell/init-helm-hoogle () + (use-package helm-hoogle + :defer t + :init + (dolist (mode haskell-modes) + (spacemacs/set-leader-keys-for-major-mode mode "hf" 'helm-hoogle)))) + +(defun haskell/post-init-flycheck () + (spacemacs/add-flycheck-hook 'haskell-mode)) + +(defun haskell/init-flycheck-haskell () + (use-package flycheck-haskell + :commands flycheck-haskell-configure + :init (add-hook 'flycheck-mode-hook 'flycheck-haskell-configure))) + (defun haskell/init-haskell-mode () (use-package haskell-mode :defer t @@ -138,7 +208,7 @@ ;; hooks (add-hook 'haskell-mode-hook 'spacemacs/init-haskell-mode) - (unless haskell-enable-ghc-mod-support + (unless (eq haskell-completion-backend 'ghc-mod) (add-hook 'haskell-mode-hook 'interactive-haskell-mode)) ;; prefixes @@ -154,9 +224,7 @@ ;; key bindings (defun spacemacs/haskell-process-do-type-on-prev-line () (interactive) - (if haskell-enable-ghci-ng-support - (haskell-mode-show-type-at 1) - (haskell-process-do-type 1))) + (haskell-process-do-type 1)) (dolist (mode haskell-modes) (spacemacs/set-leader-keys-for-major-mode mode @@ -177,8 +245,8 @@ "hd" 'inferior-haskell-find-haddock "hh" 'hoogle "hH" 'haskell-hoogle-lookup-from-local - "hi" (lookup-key haskell-mode-map (kbd "C-c TAB")) - "ht" (lookup-key haskell-mode-map (kbd "C-c C-t")) + "hi" 'haskell-process-do-info + "ht" 'haskell-process-do-type "hT" 'spacemacs/haskell-process-do-type-on-prev-line "hy" 'hayoo @@ -240,40 +308,7 @@ (evil-define-key 'insert haskell-interactive-mode-map (kbd "RET") 'haskell-interactive-mode-return) (evil-define-key 'normal haskell-interactive-mode-map - (kbd "RET") 'haskell-interactive-mode-return) - - ;; interactive haskell mode - (unless (or haskell-enable-ghc-mod-support - haskell-enable-ghci-ng-support) - (dolist (mode haskell-modes) - (spacemacs/set-leader-keys-for-major-mode mode - "hi" 'haskell-process-do-info - "ht" 'haskell-process-do-type))) - - ;; GHCi-ng - (when haskell-enable-ghci-ng-support - ;; haskell-process-type is set to auto, so setup ghci-ng for either case - ;; if haskell-process-type == cabal-repl - (setq haskell-process-args-cabal-repl '("--ghc-option=-ferror-spans" "--with-ghc=ghci-ng")) - ;; if haskell-process-type == GHCi - (setq haskell-process-path-ghci "ghci-ng") - ;; fixes ghci-ng for stack projects - (setq haskell-process-wrapper-function - (lambda (args) - (append args (list "--with-ghc" "ghci-ng")))) - - (dolist (mode haskell-modes) - (spacemacs/set-leader-keys-for-major-mode mode - ;; function suggested in - ;; https://github.com/chrisdone/ghci-ng#using-with-haskell-mode - "u" 'haskell-mode-find-uses - "ht" 'haskell-mode-show-type-at - "gg" 'haskell-mode-goto-loc)) - - ;; Useful to have these keybindings for .cabal files, too. - (with-eval-after-load 'haskell-cabal-mode-map - (define-key haskell-cabal-mode-map - [?\C-c ?\C-z] 'haskell-interactive-switch)))) + (kbd "RET") 'haskell-interactive-mode-return)) ;; align rules for Haskell (with-eval-after-load 'align