Skip to content

Commit

Permalink
lsp-layer configuration and building blocks for derived layers.
Browse files Browse the repository at this point in the history
See README.org for details

<<amendment 1>>
Updated some keybindings based on CONVENTIONS doc
Corrected file headers
Incorporated some immediate feedback from MaskRay

<<amendment 2>>
Corrected keybindings in README.org

<<amendment 3>>
Eliminated stray org-mode tag at table foot in README.org
Eliminated new 'l' prefix and moved bindings under 'g'

<<amendment 4>>
Updated defaults in config.el based on feedback from sebastiencs (lsp/lsp-ui dev)
- lsp-ui-sideline enabled by default
- lsp-ui-peek-expand-by-default disabled

<<amendment 5 09/04/18>>
Removed 'spacemacs/' prefix from lsp-format-buffer binding

<<amendment 6 09/04/18>>
Moved lsp-ui-peek bindings under j (jump)
Added goto bindings for new lsp-mode functions goto type definition and goto implementation

<<amendment 7 31/05/18>>
Corrected layer title in file headers
Rebased on dev tip (390462e)

<<amendment 8 03/07/18>>
Added keybindings for lsp-describe-thing-at-point,
lsp-workspace-restart, lsp-execute-code-action suggested by Yyoncho (LSP
Java)
Added avy keyboard navigation function provided by MaskRay
Reverted lsp-ui-peek to expand by default after an upstream change that
restricts expansion to current document, addressing the previous
performance issue.

<<amendment 9 04/07/18>>
Corrected keybinding for lsp-describe-thing-at-point

<<amendment 10 19/07/18>>
Rebound lsp-restart-workspace under mlq
Declared 'lsp' prefix (myrgy)
Added evil-set-command-property fix suggested by Yyoncho
Moved lsp-c-c++ layer from private branch to this PR after spending too
many hours of my life rebasing after circle CI picks up a formatting
error :)

<<amendment 11 25/07/18>>
Rebased
Bound cquery-freshen-index under lf
Bound cquery-preprocess-file under lp

<<amendment 12 01/08/18>>
Rebased
(c-c++ layer) moved semantic refactor refactor-at-point binding from mr
to mrp to prevent key binding error when semantic layer enabled

<<amendment 13 17/08/18>>
Added option to select ccls or cquery backend based on work by myrgy
Rebased on current upstream develop

<<amendment 14 20/08/18>>
Incorporated feedback from myrgy and maskray.
Corrected some duplication/inconsistencies.
Rebased.

<<amendment 15 21/08/18>>
Reduced duplication in backend config

<<amendment 16 22/08/18>>
Removed lsp-c-c++ layer example -- to be merged with c-c++ layer once
this PR is sorted

<<amendment 17 23/08/18>>
Added CHANGELOG.develop entry as per updated contribution guidelines.

<<amendment 18 24/08/18>>
Moved some keybindings as per feedback from sdwolfz
  • Loading branch information
Cormac Cannon authored and sdwolfz committed Aug 23, 2018
1 parent 31f4c2a commit c122eb6
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 50 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.develop
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ This file containes the change log for the next major version of Spacemacs.
- Add support for multiple backends. Supported backends are: =megahnada=,
=eclim= and =ensime=. The default backend is =meghanada=.
- ~SPC m e e~ is now to fix error around point. Use ~SPC e~ prefix to navigate
between errorsl
between errors.
**** LSP
- Added function =spacemacs/lsp-bind-keys-for-mode= to provide a consistent set of core keybindings across LSP layers.
- Added a number of =lsp-ui= configuration variables to the layer - see [[file:./layers/+tools/lsp/README.org][LSP layer README]] for details.
**** Org
- Add package =org-journal= (Nick Anderson)
- Move clock related key bindings to ~SPC a o k~
Expand Down
2 changes: 1 addition & 1 deletion layers/+spacemacs/spacemacs-editing/packages.el
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
(defun spacemacs-editing/init-avy ()
(use-package avy
:defer t
:commands (spacemacs/avy-open-url spacemacs/avy-goto-url avy-pop-mark)
:commands (spacemacs/avy-open-url spacemacs/avy-goto-url avy-pop-mark avy-with)
:init
(progn
(setq avy-all-windows 'all-frames)
Expand Down
125 changes: 99 additions & 26 deletions layers/+tools/lsp/README.org
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,111 @@
- [[#description][Description]]
- [[#features][Features:]]
- [[#configuration][Configuration]]
- [[#derived-layers][Derived layers]]
- [[#spacemacslsp-bind-keys-for-mode-mode][=spacemacs/lsp-bind-keys-for-mode mode=]]
- [[#variables][Variables]]
- [[#diagnostics][Diagnostics]]
- [[#future-additionsimprovements][Future additions/improvements]]
- [[#make-spacemacslsp-bind-keys-for-mode-bind-conditionally][Make =spacemacs/lsp-bind-keys-for-mode= bind conditionally]]
- [[#references][References]]

* Description
This layer adds support for basic language server protocol packages speaking
[[https://microsoft.github.io/language-server-protocol/specification][language server protocol]].
This layer adds support for basic language server protocol packages speaking
[[https://microsoft.github.io/language-server-protocol/specification][language server protocol]].

Different language servers may support the language server protocol to varying degrees
and they may also provide extensions; check the language server's website for
details.
=M-x lsp-capabilities= in a LSP buffer to list capabilities of the server.

** Features:
- Cross references (definitions, references, document symbol, workspace symbol
search and others)
- Workspace-wide symbol rename
- Symbol highlighting
- Flycheck
- Completion with =company-lsp=
- Signature help with eldoc
- Symbol documentation in a child frame (=lsp-ui-doc=)

Each language server may support the language server protocol in varying degree
and they may also provide extensions, check the language server's website for
details.
=M-x lsp-capabilities= in a LSP buffer to list capabilities of the server.
- Cross references (definitions, references, document symbol, workspace symbol
search and others)
- Workspace-wide symbol rename
- Symbol highlighting
- Flycheck
- Completion with =company-lsp=
- Signature help with eldoc
- Symbol documentation in a child frame (=lsp-ui-doc=)
- Navigation using imenu

* Configuration
The LSP ecosystem is based on two packages: [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] and [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]].
Please check out their documentation.
The LSP ecosystem is based on two packages: [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] and [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]].
Please check out their documentation.

If you add =lsp-*-enable= to major mode hooks for auto initialization of
language clients, customize =lsp-project-whitelist= =lsp-project-blacklist= to
disable projects you don't want to enable LSP.

** Derived layers
A number of elisp functions have been added to facilitate development of derived layers.

*** =spacemacs/lsp-bind-keys-for-mode mode=
Binds keys to a number of lsp features useful for all/most modes for the given major mode.

The prefix conventions suggested in spacemacs CONVENTIONS.org have been observed, where appropriate.

The default bindings are listed below. Derived language server layers should extend this list.

| binding | function |
|---------+-------------------------------------------------|
| ~m = b~ | format buffer (lsp) |
|---------+-------------------------------------------------|
| ~m g i~ | goto implementation |
| ~m g t~ | goto type-definition |
| ~m g a~ | goto viewport symbol (avy) |
| ~m g m~ | browse file symbols (lsp-ui-imenu) |
| ~m g d~ | find definitions |
| ~m g r~ | find references |
| ~m g s~ | find-workspace-symbol |
| ~m g p~ | jump prev (lsp-ui-peek stack - see Note 1) |
| ~m g n~ | jump next (lsp-ui-peek stack - see Note 1) |
| ~m g f~ | jump to flycheck error |
|---------+-------------------------------------------------|
| ~m h h~ | describe thing at point |
|---------+-------------------------------------------------|
| ~m l r~ | lsp-restart-workspace |
| ~m l a~ | execute code action |
|---------+-------------------------------------------------|
| ~m r r~ | rename |
|---------+-------------------------------------------------|
| ~m T d~ | toggle documentation overlay |
| ~m T F~ | toggle documentation overlay function signature |
| ~m T s~ | toggle symbol info overlay |
| ~m T S~ | toggle symbol info overlay symbol name |
| ~m T I~ | toggle symbol info overlay duplicates |

Note 1: There is a window local jump list dedicated to cross references

** Variables
A number of configuration variables have been exposed via the LSP layer =config.el=.
Sensible defaults have been provided, however they may all be overridden in your .spacemacs, or dynamically using the bindings added
under the derived mode t prefix by =(spacemacs/lsp-bind-keys-for-mode mode)=

| Variable name | Default | Description |
|---------------------------------+---------+-------------------------------------------------------------------------------------------|
| =lsp-ui-remap-xref-keybindings= | nil | When non-nil, xref keybindings remapped to lsp-ui-peek-find-{definition,references} |
| =lsp-ui-doc-enable= | t | When non-nil, the documentation overlay is displayed |
| =lsp-ui-doc-include-signature= | nil | When nil, signature omitted from lsp-ui-doc overlay (this is usually redundant) |
| =lsp-ui-sideline-enable= | t | When non-nil, the symbol information overlay is displayed |
| =lsp-ui-sideline-show-symbol= | nil | When non-nil, the symbol information overlay includes symbol name (redundant for c-modes) |
| =lsp-ui-peek-expand-by-default= | nil | When non-nil, =lsp-ui-peek= expands file matches automatically (may degrade performance) |

** Diagnostics
If some features do not work as expected, here is a common check list.

If you add =lsp-*-enable= to major mode hooks for auto initialization of
language clients, customize =lsp-project-whitelist= =lsp-project-blacklist= to
disable projects you don't want to enable LSP.
- =M-x lsp-capabilities= If the LSP workspace is initialized correctly
- =M-: xref-backend-functions= should be =(lsp--xref-backend)= for cross
references
- =M-: completion-at-point-functions= should be =(lsp-completion-at-point)= for
completion

If some features doe not work as intended, here is a common check list.
* Future additions/improvements
** Make =spacemacs/lsp-bind-keys-for-mode= bind conditionally
i.e. only bind keys if the language server supports the capability (queried using =lsp-capabilities=).
=lsp-capabilities= uses current buffer to determine the language server, so this would probably entail adding the bindings dynamically in
a mode hook.

- =M-x lsp-capabilities= If the LSP workspace is initialized correctly
- =M-: xref-backend-functions= should be =(lsp--xref-backend)= for cross
references
- =M-: completion-at-point-functions= should be =(lsp-completion-at-point)= for
completion
* References
- [[https://github.com/emacs-lsp/lsp-mode][lsp-mode repo]]
- [[https://github.com/emacs-lsp/lsp-ui][lsp-ui repo]]
19 changes: 19 additions & 0 deletions layers/+tools/lsp/config.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
;;; config.el --- Language Server Protocol Layer config file for Spacemacs
;;
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
;;
;; Author: Fangrui Song <i@maskray.me>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3

;; ;; These all have toggles bound under 't' in spacemacs/lsp-define-keys-for-mode
(defvar lsp-ui-remap-xref-keybindings nil "When non-nil, xref keybindings remapped to lsp-ui-peek-find-*")
(defvar lsp-ui-peek-expand-by-default t "Expand lsp-ui-peek by default (may have performance implications)")
(defvar lsp-ui-doc-enable t "Enable/disable lsp-ui-doc overlay")
(defvar lsp-ui-doc-include-signature nil "When non-nil, type signature included in the lsp-ui-doc overlay")
(defvar lsp-ui-sideline-enable t "Enable/disable lsp-ui-sideline overlay")
(defvar lsp-ui-sideline-show-symbol nil "When non-nil, sideline includes symbol info (largely redundant for c modes)") ; don't show symbol on the right of info
(defvar lsp-ui-sideline-ignore-duplicate t "Ignore duplicates")
97 changes: 96 additions & 1 deletion layers/+tools/lsp/funcs.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;;; packages.el --- Language Server Protocol functions File for Spacemacs
;;; funcs.el --- Language Server Protocol Layer functions file for Spacemacs
;;
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
;;
Expand Down Expand Up @@ -46,3 +46,98 @@ https://github.com/emacs-lsp/lsp-javascript/issues/9#issuecomment-379515379"
(all-completions (company-grab-symbol) candidates)))
(make-local-variable 'company-transformers)
(add-to-list 'company-transformers 'lsp-prefix-company-transformer))

(defun spacemacs/lsp-bind-keys-for-mode (mode)
"Define key bindings for the specific MODE."
(spacemacs/declare-prefix-for-mode mode "m=" "format")
(spacemacs/declare-prefix-for-mode mode "mg" "goto")
(spacemacs/declare-prefix-for-mode mode "mh" "hierarchy")
(spacemacs/declare-prefix-for-mode mode "ml" "lsp/backend")
(spacemacs/declare-prefix-for-mode mode "mr" "refactor")
(spacemacs/declare-prefix-for-mode mode "mT" "toggle")

(spacemacs/set-leader-keys-for-major-mode mode
;;format
"=b" #'lsp-format-buffer
;;goto
"gi" #'lsp-goto-implementation
"gt" #'lsp-goto-type-definition
"ga" #'spacemacs/lsp-avy-document-symbol
"gm" #'lsp-ui-imenu
"gd" #'lsp-ui-peek-find-definitions
"gr" #'lsp-ui-peek-find-references
"gs" #'lsp-ui-peek-find-workspace-symbol
"gp" #'lsp-ui-peek-jump-backward
"gn" #'lsp-ui-peek-jump-forward
"gf" #'lsp-ui-flycheck-list
;;hierarchy
"hh" #'lsp-describe-thing-at-point
;;jump
;;lsp/backend
"la" #'lsp-execute-code-action
"lr" #'lsp-restart-workspace
;;refactor
"rr" #'lsp-rename
;;toggles
"Td" #'lsp-ui-doc-mode
"Ts" #'lsp-ui-sideline-mode
"TF" #'spacemacs/lsp-ui-doc-func
"TS" #'spacemacs/lsp-ui-sideline-symb
"TI" #'spacemacs/lsp-ui-sideline-ignore-duplicate
)

)


(defun spacemacs/lsp-ui-doc-func ()
"Toggle the function signature in the lsp-ui-doc overlay"
(interactive)
(setq lsp-ui-doc-include-signature (not lsp-ui-doc-include-signature)))

(defun spacemacs/lsp-ui-sideline-symb ()
"Toggle the symbol in the lsp-ui-sideline overlay.
(generally redundant in C modes)"
(interactive)
(setq lsp-ui-sideline-show-symbol (not lsp-ui-sideline-show-symbol)))

(defun spacemacs/lsp-ui-sideline-ignore-duplicate ()
"Toggle ignore duplicates for lsp-ui-sideline overlay"
(interactive)
(setq lsp-ui-sideline-ignore-duplicate (not lsp-ui-sideline-ignore-duplicate)))

;; Used for lsp-ui-peek-mode, but may be able to use some spacemacs fn. instead?
(defun spacemacs/lsp-define-key (keymap key def &rest bindings)
"Define multiple key bindings with KEYMAP KEY DEF BINDINGS."
(interactive)
(while key
(define-key keymap (kbd key) def)
(setq key (pop bindings)
def (pop bindings))))

;; From https://github.com/MaskRay/Config/blob/master/home/.config/doom/autoload/misc.el#L118
;;;###autoload
(defun spacemacs/lsp-avy-document-symbol ()
(interactive)
(let (ranges point0 point1 (line 0) (col 0) (w (selected-window)) candidates)
(save-excursion
(goto-char 1)
(dolist (loc
(lsp--send-request (lsp--make-request
"textDocument/documentSymbol"
;;;;;; I added :all t in ccls to return all symbols in the document
`(:textDocument ,(lsp--text-document-identifier) :all t))))
(let ((range (->> loc (gethash "location") (gethash "range"))))
(-let* [((&hash "line" l0 "character" c0) (gethash "start" range))
((&hash "line" l1 "character" c1) (gethash "end" range))]
(when (or (< line l0) (and (= line l0) (<= col c0)))
(forward-line (- l0 line))
(forward-char c0)
(setq point0 (point))
(forward-line (- l1 l0))
(forward-char c1)
(setq point1 (point))
(setq line l1 col c1)
(push `((,point0 . ,point1) . ,w) candidates))))))
(avy-with avy-document-symbol
(avy--process candidates
(avy--style-fn avy-style)))))
67 changes: 46 additions & 21 deletions layers/+tools/lsp/packages.el
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
;;; packages.el --- Language Server Protocol packages File for Spacemacs
;;; packages.el --- Language Server Protocol Layer packages file for Spacemacs
;;
;; Copyright (c) 2012-2018 Sylvain Benner & Contributors
;;
Expand All @@ -11,15 +11,26 @@

(defconst lsp-packages
'(
(company-lsp :requires company)
;; `flycheck-lsp' does not exist so we defined it as built-in to avoid
;; fetching it from ELPA repositories.
;; this logical package serves to hook all flycheck related configuration
;; for LSP.
(flycheck-lsp :requires flycheck :location built-in)
lsp-mode
lsp-ui
))
lsp-mode
lsp-ui
(lsp-imenu :requires imenu :location built-in)
(lsp-ui-imenu :requires lsp-imenu :location built-in)
(company-lsp :requires company)
;; `flycheck-lsp' does not exist so we defined it as built-in to avoid
;; fetching it from ELPA repositories.
;; this logical package serves to hook all flycheck related configuration
;; for LSP.
(flycheck-lsp :requires flycheck :location built-in)
))

(defun lsp/init-lsp-mode ()
(use-package lsp-mode
:defer t
:config
(progn
(spacemacs|hide-lighter lsp-mode)
(evil-set-command-property 'lsp-goto-type-definition :jump t)
(evil-set-command-property 'lsp-goto-implementation :jump t))))

(defun lsp/init-company-lsp ()
(use-package company-lsp
Expand All @@ -28,26 +39,40 @@
;; Language servers have better idea filtering and sorting,
;; don't filter results on the client side.
(setq company-transformers nil
company-lsp-async t
company-lsp-cache-candidates nil)))
company-lsp-async t
company-lsp-cache-candidates nil)))

(defun lsp/init-flycheck-lsp ()
;; Disable lsp-flycheck.el in favor of lsp-ui-flycheck.el
(setq lsp-enable-flycheck nil))

(defun lsp/init-lsp-mode ()
(use-package lsp-mode
:defer t
:config
(progn
(spacemacs|hide-lighter lsp-mode))))

(defun lsp/init-lsp-ui ()
(use-package lsp-ui
:defer t
:init (add-hook 'lsp-mode-hook #'lsp-ui-mode)
:config
(progn
(spacemacs//lsp-sync-peek-face)
(add-hook 'spacemacs-post-theme-change-hook
#'spacemacs//lsp-sync-peek-face))))
(add-hook 'spacemacs-post-theme-change-hook #'spacemacs//lsp-sync-peek-face)

(if lsp-ui-peek-expand-by-default
(setq lsp-ui-peek-expand-function (lambda (xs) (mapcar #'car xs))))

(if lsp-ui-remap-xref-keybindings
(progn (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
(define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)))

(spacemacs/lsp-define-key
lsp-ui-peek-mode-map
"h" #'lsp-ui-peek--select-prev-file
"j" #'lsp-ui-peek--select-next
"k" #'lsp-ui-peek--select-prev
"l" #'lsp-ui-peek--select-next-file
)
)))

(defun lsp/init-lsp-imenu ()
(use-package lsp-imenu :defer t :init (add-hook 'lsp-after-open-hook #'lsp-enable-imenu)))

(defun lsp/init-lsp-ui-imenu ()
(use-package lsp-ui-imenu :defer t :config (evil-make-overriding-map lsp-ui-imenu-mode-map)))

0 comments on commit c122eb6

Please sign in to comment.