Skip to content

Commit 63baa1a

Browse files
committed
Rework customization to use lsp-mode's functionality
This does two things: - Create `defcustom` variables for settings, matching the vscode extension for all the language server settings. - Use `lsp-mode`'s support for custom settings to handle sending them to the server. This lets users persistently configure their server settings using normal Emacs setting customization. This will probably break people, so should maybe be a major version bump. Fixes #69, #75, #78; supersedes the (excellent) #74 and #76. Notes: - I have not copied the settings from the vscode extension regarding starting the server. We don't try and get prebuilt binaries, so I thought it was simplest to stick to command-and-arguments. - I renamed the process option for consistency with other servers and to avoid references to `hie`. This PR will already break basically everyone, so I thought I might as well do that too. - The current customization functions re-send a `didChangeConfiguration` notification when called. `lsp-mode` does *not* currently do this when you change variables, which is annoying. My inclination is to let them fix it in the name of simplicity, but if anyone really hates not being able to change the formatter without restarting the server I can try and hack something together.
1 parent 17d7d4c commit 63baa1a

File tree

2 files changed

+114
-206
lines changed

2 files changed

+114
-206
lines changed

README.md

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ lsp-haskell
44
[![MELPA](https://melpa.org/packages/lsp-haskell-badge.svg)](https://melpa.org/#/lsp-haskell) [![Build Status](https://travis-ci.com/emacs-lsp/lsp-haskell.svg?branch=master)](https://travis-ci.com/emacs-lsp/lsp-haskell)
55

66
An Emacs Lisp library for interacting with
7-
a [haskell-ide-engine](https://github.com/haskell/haskell-ide-engine/)
8-
server using Microsoft's
7+
a Haskell language server such as [`haskell-language-server`](https://github.com/haskell/haskell-langauge-server/)
8+
or [`ghcide`](https://github.com/haskell/ghcide/)
9+
using Microsoft's
910
[Language Server Protocol](https://github.com/Microsoft/language-server-protocol/).
1011

11-
The library is designed to integrate with existing Emacs IDE frameworks
12-
(completion-at-point, xref (beginning with Emacs 25.1), flycheck, haskell-mode, intero, etc).
13-
14-
15-
*This package is still under development, and is not recommended for daily use.*
12+
The library acts as a client for [`lsp-mode`](https://github.com/emacs-lsp/lsp-mode).
1613

1714
## Emacs Configuration
1815

@@ -27,51 +24,27 @@ this repository, or install from MELPA. Add the following to your `.emacs`:
2724

2825
Note: All three packages are also available via MELPA.
2926

30-
It needs the HIE server in your path, so follow the appropriate
27+
It needs the Haskell language server that you plan to use in your path, so follow the appropriate
3128
OSX or Linux section below accordingly.
3229

33-
## Hie Installation (OSX, Linux)
30+
## Language server installation
3431

35-
The following steps are recommended to bootstrap `lsp-haskell` on OSX.
36-
37-
```bash
38-
git clone https://github.com/haskell/haskell-ide-engine
39-
cd haskell-ide-engine
40-
./install.hs hie
41-
```
42-
43-
After this, we need to instruct Emacs to prefer `hie-wrapper` over
44-
`hie` so Hie can infer which version of ghc we need for a particular
45-
project.
46-
47-
```elisp
48-
(setq lsp-haskell-process-path-hie "hie-wrapper")
49-
```
32+
Follow the instructions on the [`haskell-language-server`](https://github.com/haskell/haskell-language-server)
33+
or [`ghcide`](https://github.com/haskell/ghcide/) repositories to install your server of choice.
5034

51-
## Per project configuration
35+
If you have installed a server other than `haskell-language-server`, make sure to
36+
customize the `lsp-haskell-server-path` variable to point to the executable you
37+
have installed (see below).
5238

53-
HIE has some settings that can be changed on the fly. These are
54-
exposed via a set of interactive functions.
39+
## Server configuration
5540

56-
- `lsp-haskell-set-hlint-on` / `lsp-haskell-set-hlint-off` Turn hlint
57-
checks on or off.
58-
- `lsp-haskell-set-max-number-of-problems` Set the maximum number of
59-
diagnostics reported.
60-
- `lsp-haskell-set-liquid-on` / `lsp-haskell-set-liquid-off` Turn
61-
liquid haskell checks on save on or off.
62-
- `lsp-haskell-set-completion-snippets-on` /
63-
`lsp-haskell-set-completion-snippets-off` Whether completion should
64-
return plain text or snippets.
65-
- `lsp-haskell-set-formatter-brittany` /
66-
`lsp-haskell-set-formatter-floskell` /
67-
`lsp-haskell-set-formatter-ormolu` Set code formatter.
41+
`lsp-haskell` exposes a number of configuration options under the `lsp-haskell`
42+
customization group, which should be set like normal customization variables.
43+
Use `M-x customize-group` to get started.
6844

69-
There are also non-interactive versions that do not actually send the
70-
settings to the live server, but are suitable for use in `.dir-locals`
71-
for a specific project.
45+
This includes a few options for for setting the server executable
46+
and arguments, and numerous settings for configuring the server itself (`hlint`,
47+
choice of formatting provider, etc.).
7248

73-
- `lsp-haskell-set-hlint`
74-
- `lsp-haskell-set-max-problems`
75-
- `lsp-haskell-set-liquid`
76-
- `lsp-haskell-set-completion-snippets`
77-
- `lsp-haskell-set-formatter`
49+
Note that server configuration settings will currently [not](https://github.com/emacs-lsp/lsp-mode/issues/1174)
50+
be applied until the server is restarted.

lsp-haskell.el

Lines changed: 94 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -31,38 +31,79 @@
3131
;; ---------------------------------------------------------------------
3232
;; Configuration
3333

34-
;;;###autoload
3534
(defgroup lsp-haskell nil
3635
"Customization group for ‘lsp-haskell’."
3736
:group 'lsp-mode)
3837

39-
;;;###autoload
40-
(defcustom lsp-haskell-process-path-hie
41-
;; "hie"
42-
"hie-wrapper"
43-
"The path for starting the haskell-ide-engine
44-
server. hie-wrapper exists on HIE master from 2018-06-10"
38+
;; ---------------------------------------------------------------------
39+
;; Language server options
40+
41+
;; These are registered with lsp-mode below, which handles preparing them for the server.
42+
;; Originally generated from the vscode extension's package.json using lsp-generate-bindings.
43+
;; Should ideally stay in sync with what's offered in the vscode extension.
44+
45+
(defcustom lsp-haskell-hlint-on
46+
t
47+
"Get suggestions from hlint."
4548
:group 'lsp-haskell
46-
:type '(choice (const "hie-wrapper")
47-
(const "haskell-language-server-wrapper")
48-
(const "ghcide")
49-
string))
50-
51-
;;;###autoload
52-
(defcustom lsp-haskell-process-args-hie
53-
'("-d" "-l" "/tmp/hie.log")
54-
"The arguments for starting the haskell-ide-engine server.
55-
For a debug log, use `-d -l /tmp/hie.log'."
49+
:type 'boolean)
50+
(defcustom lsp-haskell-max-number-of-problems
51+
100
52+
"Controls the maximum number of problems produced by the server."
5653
:group 'lsp-haskell
57-
:type '(repeat (string :tag "Argument")))
54+
:type 'number)
55+
(defcustom
56+
lsp-haskell-diagnostics-on-change
57+
t
58+
"Compute diagnostics continuously as you type. Turn off to only generate diagnostics on file save."
59+
:group 'lsp-haskell
60+
:type 'boolean)
61+
(defcustom lsp-haskell-liquid-on
62+
nil
63+
"Get diagnostics from liquid haskell."
64+
:group 'lsp-haskell
65+
:type 'boolean)
66+
(defcustom lsp-haskell-completion-snippets-on
67+
t
68+
"Show snippets with type information when using code completion."
69+
:group 'lsp-haskell
70+
:type 'boolean)
71+
(defcustom lsp-haskell-format-on-import-on
72+
t
73+
"When adding an import, use the formatter on the result."
74+
:group 'lsp-haskell
75+
:type 'boolean)
76+
(defcustom lsp-haskell-formatting-provider
77+
"ormolu"
78+
"The formatter to use when formatting a document or range."
79+
:group 'lsp-haskell
80+
:type '(choice (const :tag "brittany" "brittany")
81+
(const :tag "floskell" "floskell")
82+
(const :tag "fourmolu" "fourmolu")
83+
(const :tag "ormolu" "ormolu")
84+
(const :tag "stylish-haskell" "stylish-haskell")
85+
(const :tag "none" "none")))
5886

59-
;;;###autoload
60-
(defcustom lsp-haskell-process-wrapper-function
61-
#'identity
62-
"Use this to wrap the haskell-ide-engine process started by lsp-haskell.
87+
;; ---------------------------------------------------------------------
88+
;; Non-language server options
6389

64-
For example, use the following the start the hie process in a nix-shell:
90+
(defcustom lsp-haskell-server-path
91+
"haskell-language-server"
92+
"The language server executable. Can be something on the $PATH (e.g. 'ghcide') or a path to an executable itself."
93+
:group 'lsp-haskell
94+
:type 'string)
95+
96+
(defcustom lsp-haskell-server-args
97+
'("-d" "-l" "/tmp/hls.log")
98+
"The arguments for starting the language server.
99+
For a debug log when using haskell-language-server, use `-d -l /tmp/hls.log'."
100+
:group 'lsp-haskell
101+
:type '(repeat (string :tag "Argument")))
65102

103+
(defcustom lsp-haskell-server-wrapper-function
104+
#'identity
105+
"Use this to wrap the language server process started by lsp-haskell.
106+
For example, use the following the start the process in a nix-shell:
66107
(lambda (argv)
67108
(append
68109
(append (list \"nix-shell\" \"-I\" \".\" \"--command\" )
@@ -76,11 +117,6 @@ For example, use the following the start the hie process in a nix-shell:
76117
(function-item :tag "None" :value identity)
77118
(function :tag "Custom function")))
78119

79-
;; ---------------------------------------------------------------------
80-
;; Internal variables
81-
82-
(defvar lsp-haskell--config-options (make-hash-table))
83-
84120
;; ---------------------------------------------------------------------
85121
;; HaRe functions
86122

@@ -150,6 +186,7 @@ For example, use the following the start the hie process in a nix-shell:
150186
:pos ,(lsp-point-to-position (point))))))
151187

152188
;; ---------------------------------------------------------------------
189+
;; Miscellaneous useful functions
153190

154191
(defun lsp-haskell--session-cabal-dir ()
155192
"Get the session cabal-dir."
@@ -176,144 +213,42 @@ if projectile way fails"
176213
dir))))
177214

178215
;; ---------------------------------------------------------------------
179-
180-
(defun lsp--haskell-hie-command ()
181-
"Comamnd and arguments for launching the inferior hie process.
182-
These are assembled from the customizable variables
183-
`lsp-haskell-process-path-hie' and
184-
`lsp-haskell-process-args-hie'. If the hie executable is
185-
installed via its Makefile, there will be compiler-specific
186-
versions with names like 'hie-8.0.2' or 'hie-8.2.2'."
187-
(append (list lsp-haskell-process-path-hie "--lsp") lsp-haskell-process-args-hie) )
188-
189-
;; ---------------------------------------------------------------------
190-
216+
;; Starting the server and registration with lsp-mode
217+
218+
(defun lsp-haskell--server-command ()
219+
"Command and arguments for launching the inferior language server process.
220+
These are assembled from the customizable variables `lsp-haskell-server-path'
221+
and `lsp-haskell-server-args'."
222+
(append (list lsp-haskell-server-path "--lsp") lsp-haskell-server-args) )
223+
224+
;; Register all the language server settings with lsp-mode.
225+
;; Note that customizing these will currently *not* send the updated configuration to the server,
226+
;; users must manually restart. See https://github.com/emacs-lsp/lsp-mode/issues/1174.
227+
(lsp-register-custom-settings '(
228+
("haskell.formattingProvider" lsp-haskell-formatting-provider)
229+
("haskell.formatOnImportOn" lsp-haskell-format-on-import-on t)
230+
("haskell.completionSnippetsOn" lsp-haskell-completion-snippets-on t)
231+
("haskell.liquidOn" lsp-haskell-liquid-on t)
232+
("haskell.diagnosticsOnChange" lsp-haskell-diagnostics-on-change t)
233+
("haskell.maxNumberOfProblems" lsp-haskell-max-number-of-problems)
234+
("haskell.hlintOn" lsp-haskell-hlint-on t)))
235+
236+
;; Register the client itself
191237
(lsp-register-client
192238
(make-lsp--client
193-
:new-connection (lsp-stdio-connection (lambda () (lsp-haskell--hie-command)))
239+
:new-connection (lsp-stdio-connection (lambda () (lsp-haskell--server-command)))
194240
:major-modes '(haskell-mode)
195-
:server-id 'hie
241+
;; This is arbitrary.
242+
:server-id 'lsp-haskell
243+
;; We need to manually pull out the configuration section and set it. Possibly in
244+
;; the future lsp-mode will asssociate servers with configuration sections more directly.
196245
:initialized-fn (lambda (workspace)
197246
(with-lsp-workspace workspace
198-
(lsp-haskell--set-configuration)))
199-
;; :multi-root t
200-
;; :initialization-options 'lsp-haskell--make-init-options
247+
(lsp--set-configuration (lsp-configuration-section "haskell"))))
248+
;; No need to set :language-id, since there isn't one for Haskell and we
249+
;; don't support multiple languages
201250
))
202251

203-
(defun lsp-haskell--hie-command ()
204-
(funcall lsp-haskell-process-wrapper-function (lsp--haskell-hie-command)))
205-
206-
(cl-defmethod lsp-initialization-options ((_server (eql hie)))
207-
"Initialization options for haskell."
208-
`(:languageServerHaskell ,lsp-haskell--config-options))
209-
210-
;; ---------------------------------------------------------------------
211-
212-
(defun lsp-haskell--set-configuration ()
213-
(lsp--set-configuration `(:languageServerHaskell ,lsp-haskell--config-options)))
214-
215-
(defun lsp-haskell-set-config (name option)
216-
"Set config option NAME to value OPTION in the haskell lsp server."
217-
(puthash name option lsp-haskell--config-options))
218-
219-
;; parseJSON = withObject "Config" $ \v -> do
220-
;; s <- v .: "languageServerHaskell"
221-
;; flip (withObject "Config.settings") s $ \o -> Config
222-
;; <$> o .:? "hlintOn" .!= True
223-
;; <*> o .:? "maxNumberOfProblems" .!= 100
224-
;; <*> o .:? "liquidOn" .!= False
225-
;; <*> o .:? "completionSnippetsOn" .!= True
226-
227-
;; -------------------------------------
228-
229-
(defun lsp-haskell-set-hlint (val)
230-
"Enable(t)/Disable(nil) running hlint."
231-
(lsp-haskell-set-config "hlintOn" val))
232-
233-
(defun lsp-haskell-set-hlint-on ()
234-
"Enable running hlint haskell."
235-
(interactive)
236-
(lsp-haskell-set-hlint t)
237-
(lsp-haskell--set-configuration))
238-
239-
(defun lsp-haskell-set-hlint-off ()
240-
"Disable running hlint."
241-
(interactive)
242-
(lsp-haskell-set-hlint :json-false)
243-
(lsp-haskell--set-configuration))
244-
245-
;; -------------------------------------
246-
247-
(defun lsp-haskell-set-max-problems (val)
248-
"Set maximum number of problems reported to VAL."
249-
(lsp-haskell-set-config "maxNumberOfProblems" val))
250-
251-
(defun lsp-haskell-set-max-number-of-problems (val)
252-
"Set maximum number of problems reported to VAL."
253-
(interactive "nMax number of problems to report: ")
254-
(lsp-haskell-set-max-problems val)
255-
(lsp-haskell--set-configuration))
256-
257-
;; -------------------------------------
258-
259-
(defun lsp-haskell-set-liquid (val)
260-
"Enable(t)/Disable(nil) running liquid haskell on save."
261-
(lsp-haskell-set-config "liquidOn" val))
262-
263-
(defun lsp-haskell-set-liquid-on ()
264-
"Enable running liquid haskell on save."
265-
(interactive)
266-
(lsp-haskell-set-liquid t)
267-
(lsp-haskell--set-configuration))
268-
269-
(defun lsp-haskell-set-liquid-off ()
270-
"Disable running liquid haskell on save."
271-
(interactive)
272-
(lsp-haskell-set-liquid :json-false)
273-
(lsp-haskell--set-configuration))
274-
275-
;; -------------------------------------
276-
277-
(defun lsp-haskell-set-completion-snippets (val)
278-
"Enable(t)/Disable(nil) providing completion snippets."
279-
(lsp-haskell-set-config "completionSnippetsOn" val))
280-
281-
(defun lsp-haskell-set-completion-snippets-on ()
282-
"Enable providing completion snippets."
283-
(interactive)
284-
(lsp-haskell-set-completion-snippets t)
285-
(lsp-haskell--set-configuration))
286-
287-
(defun lsp-haskell-set-completion-snippets-off ()
288-
"Disable providing completion snippets."
289-
(interactive)
290-
(lsp-haskell-set-completion-snippets :json-false)
291-
(lsp-haskell--set-configuration))
292-
293-
;; -------------------------------------
294-
295-
(defun lsp-haskell-set-formatter (val)
296-
"Set code formatter."
297-
(lsp-haskell-set-config "formattingProvider" val))
298-
299-
(defun lsp-haskell-set-formatter-brittany ()
300-
"Use brittany."
301-
(interactive)
302-
(lsp-haskell-set-formatter :brittany)
303-
(lsp-haskell--set-configuration))
304-
305-
(defun lsp-haskell-set-formatter-floskell ()
306-
"Use floskell."
307-
(interactive)
308-
(lsp-haskell-set-formatter :floskell)
309-
(lsp-haskell--set-configuration))
310-
311-
(defun lsp-haskell-set-formatter-ormolu ()
312-
"Use ormolu."
313-
(interactive)
314-
(lsp-haskell-set-formatter :ormolu)
315-
(lsp-haskell--set-configuration))
316-
317252
;; ---------------------------------------------------------------------
318253

319254
(provide 'lsp-haskell)

0 commit comments

Comments
 (0)