#+OPTIONS: toc:nil num:nil email:t timestamp:nil #+OPTIONS: reveal_history:t reveal_control:nil #+REVEAL_ROOT: https://cdn.jsdelivr.net/reveal.js/3.0.0/ #+REVEAL_PLUGINS: (highlight notes) #+REVEAL_OPTIONS: history:t #+TITLE: lsp-mode: 30+ languages in one minor mode #+AUTHOR: George Pittarelli #+EMAIL: g@gjp.cc * Follow along These slides are generated from an org file with some speaker notes and code blocks Available at https://gjp.cc/lsp #+BEGIN_SRC sh wget gjp.cc/lsp-mode.org # or git clone github.com/gpittarelli/lsp-presentation #+END_SRC * What do we want IDE features: - auto-complete - documentation on hover - jump to definition - refactoring menu - error detection - project-wide diagnostics #+BEGIN_NOTES - intro the features we want - not exhaustive - syntax coloring, REPLs - ask audience if they're curious about anything not on the list #+END_NOTES * Implement it per-language - R: ESS - Clojure: CIDER - Typescript: tsserver and tide-mode - C#: omnisharp and omnisharp-emacs #+BEGIN_NOTES - currently, features are best integrated through language-specific tools - ESS, CIDER: build it custom for emacs; lots of highly specific code - tsserver, omnisharp: servers with emacs client implementations; but not reusable protocols #+END_NOTES * Attempts to generalize - textmate files: syntax highlighting - tags files (etags, ctags); [[https://www.gnu.org/software/global/][GNU global]] - [[https://github.com/Valloric/ycmd][ycmd]] YouCompleteMe #+BEGIN_NOTES - Technical choice <<< community support - LSP has, by far: more languages, supporting orgs, etc. #+END_NOTES * LSP: Language Server Protocol Implement language diagnostics once, as a server All editors can share the smarts file:images/lsp-grid.png Overview: https://langserver.org/ Spec: https://microsoft.github.io/language-server-protocol/specification #+BEGIN_NOTES Introduce LSP - By Microsoft, - nice that good language tools are written by a single team, and/or by motivated orgs (Microsoft, llvm, sourgegraph, omnisharp, etc.) - For M languages and N editors: - MxN -> M+N - RMS wants it https://lists.gnu.org/archive/html/emacs-devel/2017-04/msg00798.html #+END_NOTES * LSP: Overview - RPC: JSON with headers - single connection (stdio or tcp) - growing fairly fast: https://github.com/Microsoft/language-server-protocol #+BEGIN_NOTES - New sorta-like-json-over-http proto - Already on v3.8.0; small changes every few months #+END_NOTES * LSP: Overview (2) - Synchronize file/project state - Query for specific information on hover/completion - Send commands for refactors; get back text changes to apply #+BEGIN_NOTES - New sorta-like-json-over-http proto - Already on v3.8.0; small changes every few months #+END_NOTES * lsp-mode Minor mode: - implements an LSP client - hooks into standard Emacs APIs - provides API for defining clients Available on [[https://melpa.org/#/][MELPA]]: #+BEGIN_SRC elisp (require 'package) (package-initialize) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-install 'lsp-mode) #+END_SRC * lsp-mode provides - [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html][imenu]] - [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html][xref]] - [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion-in-Buffers.html][completion-at-point]] - [[https://www.emacswiki.org/emacs/ElDoc][eldoc]] - flycheck, fancy UI ([[https://github.com/emacs-lsp/lsp-ui][lsp-ui]]) - company ([[https://github.com/tigersoldier/company-lsp][company-lsp]]) #+BEGIN_NOTES - xref: - xref backends (return a symbol from hook function; and extend some generic methods with cl-defmethod specialized on the symbol) - completion-at-point: - completion-at-point-functions - Emacs does actually have lots of builtin hooks to provide IDE-esque features - Note: some of these require synchronous resolution... very troubling for some of the language servers - eg completion-at-point is sync; company backends are not (callback) - Demo time - lsp-ui - company-lsp - JavaScript, Rust, java, ruby (tcp), R, audience requests... - small aside: lsp hover fn... - Lead to next slide: Note, nothing about how to actually start or connect to the language server... #+END_NOTES * How to define a client #+BEGIN_SRC elisp (lsp-define-stdio-client lsp-css "css" lsp-css--get-root '("css-languageserver" "--stdio")) #+END_SRC #+BEGIN_NOTES Main aspects: stdio vs tcp, name, what to launch - need to configure how to setup each language - creates a lsp-(name)-enable function which starts the server and enables lsp-mode (call this, NOT lsp-mode directly) - simple example: lsp-css - complex examples: - haskell - java (example of per-language LSP endpoints) - turns out, we need some language specific smarts anyways... #+END_NOTES * language packages #+BEGIN_SRC elisp (package-install 'lsp-css) #+END_SRC #+BEGIN_NOTES Main aspects: stdio vs tcp, name, what to launch - need to configure how to setup each language - creates a lsp-(name)-enable function which starts the - simple example: lsp-css - complex examples: - haskell - java (example of per-language LSP endpoints) - turns out, we need some language specific smarts anyways... #+END_NOTES * Example setup #+BEGIN_SRC elisp (defun my-rust-mode-setup () (company-mode) (lsp-rust-enable) (eldoc-mode t) (flycheck-mode) (lsp-ui-mode)) (add-hook 'rust-mode-hook #'my-rust-mode-setup) (add-to-list 'auto-mode-alist '("\\.rs" . rust-mode)) #+END_SRC #+BEGIN_NOTES - this example is one of many from https://github.com/gpittarelli/emacs-lsp-acceptance-testing/blob/master/tests/setup.el #+END_NOTES * Testing - ert - [[https://github.com/emacs-lsp/lsp-java][lsp-java]]: cucumber tests - [[https://github.com/gpittarelli/emacs-lsp-acceptance-testing/][emacs-lsp-acceptance-tests]] #+BEGIN_NOTES - LSP is a pretty complex ecosystem of clients and servers with varying opionions on what exactly the spec entails; and all of them are developing independently. - ert: mostly unit tests; buy also can easily do integration because emacs is flexible like that - emacs-lsp-acceptance-tests: WIP; includes sample configs for most of the languages for lsp mode and #+END_NOTES * Protocol parsing in emacs - frankly, hacky (manual dechunking) - process filters and lots of manual parsing - alternative: just use buffers #+BEGIN_NOTES - I rewrote current LSP parsing loop from the previous code for perf: - https://github.com/emacs-lsp/lsp-mode/pull/107 - https://github.com/emacs-lsp/lsp-mode/pull/108 - need to be careful of any incremental copying; etc.: some servers can send back multi-mb responses in some cases! (eg, completing in the global scope in JS) - (yes, completion does support chunked responses; not all language servers support all the fine details of the protocol) - handling multibyte characters/responses is surprisingly hard (or at least, its hard to do while remaining efficient) - buffer downsides: grows big; #+END_NOTES * Future goal: autosetup Based on major mode and/or file extension, we should be able to automatically: - download the server - install the necessary lsp-plugins - provide good defaults #+BEGIN_NOTES - I sorta wanted to have this working in time for the talk... but stuff happened - have a really good name for it #+END_NOTES * Alternatives Note there's a couple defunct "emacs-lsp", etc. packages on GitHub from previous attempts by other people. eglot is the only reasonably working alternative #+BEGIN_NOTES defunct examples: - https://github.com/sourcegraph/emacs-lsp - ... i remembers there were others but can't find them eglot: https://github.com/joaotavora/eglot - more opinionated (good...?); sorta like lsp-mode + lsp-ui + a few lsp-(language) clients - less extensible (fewer defcustoms; client options are only the command to run) - no separate packages (good imo) - less bugfixes/community support #+END_NOTES