about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el2216
1 files changed, 2216 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el
new file mode 100644
index 000000000000..f03fcc909226
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180911.1829/lsp-methods.el
@@ -0,0 +1,2216 @@
+;; Copyright (C) 2016-2018  Vibhav Pant <vibhavp@gmail.com>  -*- lexical-binding: t -*-
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+(require 'cl-lib)
+(require 'json)
+(require 'xref)
+(require 'subr-x)
+(require 'widget)
+(require 'lsp-io)
+(require 'lsp-common)
+(require 'pcase)
+(require 'inline)
+(require 'em-glob)
+
+(defconst lsp--file-change-type
+  `((created . 1)
+    (changed . 2)
+    (deleted . 3)))
+
+;; A ‘lsp--client’ object describes the client-side behavior of a language
+;; server.  It is used to start individual server processes, each of which is
+;; represented by a ‘lsp--workspace’ object.  Client objects are normally
+;; created using ‘lsp-define-stdio-client’ or ‘lsp-define-tcp-client’.  Each
+;; workspace refers to exactly one client, but there can be multiple workspaces
+;; for a single client.
+(cl-defstruct lsp--client
+  ;; ‘language-id’ is a function that receives a buffer as a single argument
+  ;; and should return the language identifier for that buffer.  See
+  ;; https://microsoft.github.io/language-server-protocol/specification#textdocumentitem
+  ;; for a list of language identifiers.  Also consult the documentation for
+  ;; the language server represented by this client to find out what language
+  ;; identifiers it supports or expects.
+  (language-id nil :read-only t)
+
+  ;; send-async and send-sync are unused field, but haven't been
+  ;; removed so as to avoid breaking byte-compiled clients.
+  ;; FIXME: We shouldn’t need to take binary compatibility into account,
+  ;; especially since the ‘lsp--client’ structure is internal.  These fields
+  ;; should just be removed.
+  (send-sync nil :read-only t)
+  (send-async nil :read-only t)
+
+  ;; FIXME: This field is apparently unused and should be removed.
+  (type nil :read-only t)
+
+  ;; ‘new-connection’ is a function that should start a language server process
+  ;; and return a cons (COMMAND-PROCESS . COMMUNICATION-PROCESS).
+  ;; COMMAND-PROCESS must be a process object representing the server process
+  ;; just started.  COMMUNICATION-PROCESS must be a process (including pipe and
+  ;; network processes) that ‘lsp-mode’ uses to communicate with the language
+  ;; server using the language server protocol.  COMMAND-PROCESS and
+  ;; COMMUNICATION-PROCESS may be the same process; in that case
+  ;; ‘new-connection’ may also return that process as a single
+  ;; object. ‘new-connection’ is called with two arguments, FILTER and
+  ;; SENTINEL.  FILTER should be used as process filter for
+  ;; COMMUNICATION-PROCESS, and SENTINEL should be used as process sentinel for
+  ;; COMMAND-PROCESS.
+  (new-connection nil :read-only t)
+
+  ;; ‘stderr’ is the name of a buffer to write the standard error to.
+  ;; FIXME: ‘stderr’ should be the actual buffer, and it should be a field of
+  ;; the ‘lsp--workspace’.
+  (stderr nil :read-only t)
+
+  ;; ‘get-root’ is a function that should return the workspace root directory
+  ;; for the current buffer.  It may return either a directory name or a
+  ;; directory file name.  The ‘get-root’ function is called without arguments.
+  ;; ‘lsp-mode’ will start one server process per client and root directory.
+  ;; It passes the root directory to the ‘initialize’ method of the language
+  ;; server; see
+  ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
+  ;; Also consult the documentation of your language server for information
+  ;; about what it expects as workspace root.
+  (get-root nil :read-only t)
+
+  ;; ‘ignore-regexps’ is a list of regexps.  When a data packet from the
+  ;; language server matches any of these regexps, it will be ignored.  This is
+  ;; intended for dealing with language servers that output non-protocol data.
+  (ignore-regexps nil :read-only t)
+
+  ;; ‘ignore-messages’ is a list of regexps.  When a message from the language
+  ;; server matches any of these regexps, it will be ignored.  This is useful
+  ;; for filtering out unwanted messages; such as servers that send nonstandard
+  ;; message types, or extraneous log messages.
+  (ignore-messages nil :read-only t)
+
+  ;; ‘notification-handlers’ is a hash table mapping notification method names
+  ;; (strings) to functions handling the respective notifications.  Upon
+  ;; receiving a notification, ‘lsp-mode’ will call the associated handler
+  ;; function passing two arguments, the ‘lsp--workspace’ object and the
+  ;; deserialized notification parameters.
+  (notification-handlers (make-hash-table :test 'equal) :read-only t)
+
+  ;; ‘request-handlers’ is a hash table mapping request method names
+  ;; (strings) to functions handling the respective notifications.  Upon
+  ;; receiving a request, ‘lsp-mode’ will call the associated handler function
+  ;; passing two arguments, the ‘lsp--workspace’ object and the deserialized
+  ;; request parameters.
+  (request-handlers (make-hash-table :test 'equal) :read-only t)
+
+  ;; ‘response-handlers’ is a hash table mapping integral JSON-RPC request
+  ;; identifiers for pending asynchronous requests to functions handling the
+  ;; respective responses.  Upon receiving a response from the language server,
+  ;; ‘lsp-mode’ will call the associated response handler function with a
+  ;; single argument, the deserialized response parameters.
+  (response-handlers (make-hash-table :test 'eql) :read-only t)
+
+  ;; ‘string-renderers’ is an alist mapping MarkedString language identifiers
+  ;; (see
+  ;; https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
+  ;; to functions that can render the respective languages.  The rendering
+  ;; functions are called with a single argument, the MarkedString value.  They
+  ;; should return a propertized string with the rendered output.
+  (string-renderers '())
+  ;; ‘last-id’ is the last JSON-RPC identifier used.
+  ;; FIXME: ‘last-id’ should be in ‘lsp--workspace’.
+  (last-id 0)
+
+  ;; Function to enable the client for the current buffer, called without
+  ;; arguments.
+  (enable-function nil :read-only t)
+
+  ;; ‘prefix-function’ is called for getting the prefix for completion.
+  ;; The function takes no parameter and returns a cons (start . end) representing
+  ;; the start and end bounds of the prefix. If it's not set, the client uses a
+  ;; default prefix function."
+  (prefix-function nil :read-only t)
+
+  ;; Contains mapping of scheme to the function that is going to be used to load
+  ;; the file.
+  (uri-handlers (make-hash-table :test #'equal) :read-only t)
+  ;; ‘action-handlers’ is a hash table mapping action to a handler function. It
+  ;; can be used in `lsp-execute-code-action' to determine whether the action
+  ;; current client is interested in executing the action instead of sending it
+  ;; to the server.
+  (action-handlers (make-hash-table :test 'equal) :read-only t)
+
+  ;; ‘default-renderer’ is the renderer that is going to be used when there is
+  ;; no concrete "language" specified for the current MarkedString. (see
+  ;; https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
+  (default-renderer nil))
+
+(cl-defstruct lsp--registered-capability
+  (id "" :type string)
+  (method " " :type string)
+  (options nil))
+
+;; A ‘lsp--workspace’ object represents exactly one language server process.
+(cl-defstruct lsp--workspace
+  ;; ‘parser’ is a ‘lsp--parser’ object used to parse messages for this
+  ;; workspace.  Parsers are not shared between workspaces.
+  (parser nil :read-only t)
+
+  ;; ‘file-versions’ is a hashtable of files "owned" by the workspace.  It maps
+  ;; file names to file versions.  See
+  ;; https://microsoft.github.io/language-server-protocol/specification#versionedtextdocumentidentifier.
+  (file-versions nil :read-only t)
+
+  ;; ‘server-capabilities’ is a hash table of the language server capabilities.
+  ;; It is the hash table representation of a LSP ServerCapabilities structure;
+  ;; cf. https://microsoft.github.io/language-server-protocol/specification#initialize.
+  (server-capabilities nil)
+
+  ;; ‘registered-server-capabilities’ is a list of hash tables that represent
+  ;; dynamically-registered Registration objects.  See
+  ;; https://microsoft.github.io/language-server-protocol/specification#client_registerCapability.
+  (registered-server-capabilities nil)
+
+  ;; ‘root’ is a directory name or a directory file name for the workspace
+  ;; root.  ‘lsp-mode’ passes this directory to the ‘initialize’ method of the
+  ;; language server; see
+  ;; https://microsoft.github.io/language-server-protocol/specification#initialize.
+  (root nil :ready-only t)
+
+  ;; ‘client’ is the ‘lsp--client’ object associated with this workspace.
+  (client nil :read-only t)
+
+  ;; FIXME: ‘change-timer-disabled’ is unused and should be removed.
+  (change-timer-disabled nil)
+
+  ;; ‘proc’ is a process object; it may represent a regular process, a pipe, or
+  ;; a network connection.  ‘lsp-mode’ communicates with ‘proc’ using the
+  ;; language server protocol.  ‘proc’ corresponds to the COMMUNICATION-PROCESS
+  ;; element of the return value of the client’s ‘get-root’ field, which see.
+  (proc nil)
+
+  ;; ‘proc’ is a process object; it must represent a regular process, not a
+  ;; pipe or network process.  It represents the actual server process that
+  ;; corresponds to this workspace.  ‘cmd-proc’ corresponds to the
+  ;; COMMAND-PROCESS element of the return value of the client’s ‘get-root’
+  ;; field, which see.
+  (cmd-proc nil)
+
+  ;; ‘buffers’ is a list of buffers associated with this workspace.
+  (buffers nil)
+
+  ;; ‘highlight-overlays’ is a hash table mapping buffers to a list of overlays
+  ;; used for highlighting the symbol under point.
+  (highlight-overlays (make-hash-table :test 'eq) :read-only t)
+
+  ;; Extra client capabilities provided by third-party packages using
+  ;; `lsp-register-client-capabilities'. It's value is an alist of (PACKAGE-NAME
+  ;; . CAPS), where PACKAGE-NAME is a symbol of the third-party package name,
+  ;; and CAPS is either a plist of the client capabilities, or a function that
+  ;; takes no argument and returns a plist of the client capabilities or nil.")
+  (extra-client-capabilities nil)
+
+  ;; Workspace status
+  (status nil)
+
+  ;; ‘metadata’ is a generic storage for workspace specific data. It is
+  ;; accessed via `lsp-workspace-set-metadata' and `lsp-workspace-set-metadata'
+  (metadata (make-hash-table :test 'equal))
+
+  ;; contains all the file notification watches that have been created for the
+  ;; current workspace in format filePath->file notification handle.
+  (watches (make-hash-table :test 'equal)))
+
+(defvar lsp--workspaces (make-hash-table :test #'equal)
+  "Table of known workspaces, indexed by the project root directory.")
+
+(defvar lsp--ignored-workspace-roots (make-hash-table :test #'equal)
+  "Table of project roots which should not have a workspace,
+indexed by the project root directory.
+
+This is populated when the user declines to open a workspace
+for a file in the workspace.")
+
+(defcustom lsp-render-markdown-markup-content nil
+  "Function to be use for rendering MarkupContent.
+
+It should take two arguments - a string denoting the type of markup content
+and a string containing the text to be rendered.  The returned value should
+be a string that may be fontified/propertized.
+
+When nil, MarkupContent is rendered as plain text."
+  :type 'function
+  :group 'lsp-mode)
+
+(defcustom lsp-before-initialize-hook nil
+  "List of functions to be called before a Language Server has been initialized
+for a new workspace."
+  :type 'hook
+  :group 'lsp-mode)
+
+(defcustom lsp-after-initialize-hook nil
+  "List of functions to be called after a Language Server has been initialized
+for a new workspace."
+  :type 'hook
+  :group 'lsp-mode)
+
+(defcustom lsp-before-open-hook nil
+  "List of functions to be called before a new file with LSP support is opened."
+  :type 'hook
+  :group 'lsp-mode)
+
+(defcustom lsp-after-open-hook nil
+  "List of functions to be called after a new file with LSP support is opened."
+  :type 'hook
+  :group 'lsp-mode)
+
+(defvar lsp--sync-methods
+  '((0 . none)
+    (1 . full)
+    (2 . incremental)))
+(defvar-local lsp--server-sync-method nil
+  "Sync method recommended by the server.")
+
+;;;###autoload
+(defgroup lsp-mode nil
+  "Customization group for ‘lsp-mode’."
+  :group 'tools)
+
+;;;###autoload
+(defgroup lsp-faces nil
+  "Faces for ‘lsp-mode’."
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-document-sync-method nil
+  "How to sync the document with the language server."
+  :type '(choice (const :tag "Documents should not be synced at all." 'none)
+                 (const :tag "Documents are synced by always sending the full content of the document." 'full)
+                 (const :tag "Documents are synced by always sending incremental changes to the document." 'incremental)
+                 (const :tag "Use the method recommended by the language server." nil))
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-project-blacklist nil
+  "A list of project directory regexps for which LSP shouldn't be initialized.
+LSP should be initialized if the given project root matches one pattern in the
+whitelist, or does not match any pattern in the blacklist."
+  :type '(repeat regexp)
+  :group 'lsp-mode)
+
+(defcustom lsp-project-whitelist nil
+  "A list of project directory regexps for which LSP should be initialized."
+  :type '(repeat regexp)
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-enable-eldoc t
+  "Enable `eldoc-mode' integration."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-eldoc-render-all t
+  "Define whether all of the returned by document/onHover will be displayed.
+
+If `lsp-markup-display-all' is set to nil `eldoc' will show only
+the symbol information."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-highlight-symbol-at-point t
+  "Highlight the symbol under the point."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-enable-codeaction t
+  "Enable code action processing."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-enable-completion-at-point t
+  "Enable `completion-at-point' integration."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-enable-xref t
+  "Enable xref integration."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-enable-indentation t
+  "Indent regions using the file formatting functionality provided by the language server."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-before-save-edits t
+  "If non-nil, `lsp-mode' will apply edits suggested by the language server
+before saving a document."
+  :type 'boolean
+  :group 'lsp-mode)
+
+;;;###autoload
+(defcustom lsp-hover-text-function 'lsp--text-document-hover-string
+  "The LSP method to use to display text on hover."
+  :type '(choice (function :tag "textDocument/hover"
+                           lsp--text-document-hover-string)
+                 (function :tag "textDocument/signatureHelp"
+                           lsp--text-document-signature-help))
+  :group 'lsp-mode)
+
+;;;###autoload
+(defface lsp-face-highlight-textual
+  '((((background dark))  :background "saddle brown")
+    (((background light)) :background "yellow"))
+  "Face used for textual occurances of symbols."
+  :group 'lsp-faces)
+
+;;;###autoload
+(defface lsp-face-highlight-read
+  '((((background dark))  :background "firebrick")
+    (((background light)) :background "red"))
+  "Face used for highlighting symbols being read."
+  :group 'lsp-faces)
+
+;;;###autoload
+(defface lsp-face-highlight-write
+  '((((background dark))  :background "sea green")
+     (((background light)) :background "green"))
+  "Face used for highlighting symbols being written to."
+  :group 'lsp-faces)
+
+(defun lsp-client-register-uri-handler (client scheme handler)
+  (cl-check-type client lsp--client)
+  (cl-check-type scheme string)
+  (cl-check-type handler function)
+  (puthash scheme handler (lsp--client-uri-handlers client)))
+
+(defun lsp-client-on-notification (client method callback)
+  (cl-check-type client lsp--client)
+  (cl-check-type method string)
+  (cl-check-type callback function)
+  (puthash method callback (lsp--client-notification-handlers client)))
+
+(defun lsp-client-on-request (client method callback)
+  (cl-check-type client lsp--client)
+  (cl-check-type method string)
+  (cl-check-type callback function)
+  (puthash method callback (lsp--client-request-handlers client)))
+
+(defun lsp-client-on-action (client method callback)
+  (cl-check-type client lsp--client)
+  (cl-check-type method string)
+  (cl-check-type callback function)
+  (puthash method callback (lsp--client-action-handlers client)))
+
+(defun lsp-workspace-set-metadata (key value &optional workspace)
+  "Associate KEY with VALUE in the WORKSPACE metadata.
+If WORKSPACE is not provided current workspace will be used."
+  (puthash key value (lsp--workspace-metadata (or workspace lsp--cur-workspace))))
+
+(defun lsp-workspace-get-metadata (key &optional workspace)
+  "Lookup KEY in WORKSPACE metadata.
+If WORKSPACE is not provided current workspace will be used."
+  (gethash key (lsp--workspace-metadata (or workspace lsp--cur-workspace))))
+
+(define-inline lsp--make-request (method &optional params)
+  "Create request body for method METHOD and parameters PARAMS."
+  (inline-quote
+    (plist-put (lsp--make-notification ,method ,params)
+      :id (cl-incf (lsp--client-last-id (lsp--workspace-client lsp--cur-workspace))))))
+
+(defun lsp-make-request (method &optional params)
+  "Create request body for method METHOD and parameters PARAMS."
+  (lsp--make-request method params))
+
+(defun lsp--make-response-error (code message data)
+  (cl-check-type code number)
+  (cl-check-type message string)
+  `(:code ,code :message ,message :data ,data))
+
+(defun lsp--make-response (id result error)
+  (cl-check-type error list)
+  `(:jsonrpc "2.0" :id ,id :result ,result :error ,error))
+
+(define-inline lsp--make-notification (method &optional params)
+  "Create notification body for method METHOD and parameters PARAMS."
+  (inline-quote
+    (progn (cl-check-type ,method string)
+      (list :jsonrpc "2.0" :method ,method :params ,params))))
+
+;; Define non-inline public aliases to avoid breaking binary compatibility.
+(defun lsp-make-notification (method &optional params)
+  "Create notification body for method METHOD and parameters PARAMS."
+  (lsp--make-notification method params))
+
+(define-inline lsp--make-message (params)
+  "Create a LSP message from PARAMS, after encoding it to a JSON string."
+  (inline-quote
+    (let* ((json-encoding-pretty-print lsp-print-io)
+           (json-false :json-false)
+           (body (json-encode ,params)))
+      (format "Content-Length: %d\r\n\r\n%s" (string-bytes body) body))))
+
+(define-inline lsp--send-notification (body)
+  "Send BODY as a notification to the language server."
+  (inline-quote
+    (lsp--send-no-wait
+      (lsp--make-message ,body)
+      (lsp--workspace-proc lsp--cur-workspace))))
+
+(defun lsp-send-notification (body)
+  "Send BODY as a notification to the language server."
+  (lsp--send-notification body))
+
+(define-inline lsp--cur-workspace-check ()
+  (inline-quote
+    (progn
+      (cl-assert lsp--cur-workspace nil
+        "No language server is associated with this buffer.")
+      (cl-assert (lsp--workspace-p lsp--cur-workspace)))))
+
+(define-inline lsp--cur-parser ()
+  (inline-quote (lsp--workspace-parser lsp--cur-workspace)))
+
+(defun lsp--send-request (body &optional no-wait)
+  "Send BODY as a request to the language server, get the response.
+If NO-WAIT is non-nil, don't synchronously wait for a response."
+  (let* ((parser (lsp--cur-parser))
+          (message (lsp--make-message body))
+          (process (lsp--workspace-proc lsp--cur-workspace)))
+    (setf (lsp--parser-waiting-for-response parser) (not no-wait))
+    (if no-wait
+      (lsp--send-no-wait message process)
+      (lsp--send-wait message process parser))
+    (when (not no-wait)
+      (prog1 (lsp--parser-response-result parser)
+        (setf (lsp--parser-response-result parser) nil)))))
+
+(defalias 'lsp-send-request 'lsp--send-request
+  "Send BODY as a request to the language server and return the response synchronously.
+
+\n(fn BODY)")
+
+(defun lsp--send-request-async (body callback)
+  "Send BODY as a request to the language server, and call CALLBACK with
+the response recevied from the server asynchronously."
+  (let ((client (lsp--workspace-client lsp--cur-workspace))
+        (id (plist-get body :id)))
+    (cl-assert id nil "body missing id field")
+    (puthash id callback (lsp--client-response-handlers client))
+    (lsp--send-no-wait (lsp--make-message body)
+      (lsp--workspace-proc lsp--cur-workspace))
+    body))
+
+(defalias 'lsp-send-request-async 'lsp--send-request-async)
+
+(define-inline lsp--inc-cur-file-version ()
+  (inline-quote (cl-incf (gethash (current-buffer)
+                           (lsp--workspace-file-versions lsp--cur-workspace)))))
+
+(define-inline lsp--cur-file-version ()
+  "Return the file version number.  If INC, increment it before."
+  (inline-quote
+    (gethash (current-buffer) (lsp--workspace-file-versions lsp--cur-workspace))))
+
+(define-inline lsp--make-text-document-item ()
+  "Make TextDocumentItem for the currently opened file.
+
+interface TextDocumentItem {
+    uri: string; // The text document's URI
+    languageId: string; // The text document's language identifier.
+    version: number;
+    text: string;
+}"
+  (inline-quote
+    (let ((language-id-fn (lsp--client-language-id (lsp--workspace-client lsp--cur-workspace))))
+      (list :uri (lsp--buffer-uri)
+	      :languageId (funcall language-id-fn (current-buffer))
+	      :version (lsp--cur-file-version)
+	      :text (buffer-substring-no-properties (point-min) (point-max))))))
+
+;; Clean up the entire state of lsp mode when Emacs is killed, to get rid of any
+;; pending language servers.
+(add-hook 'kill-emacs-hook #'lsp--global-teardown)
+
+(defun lsp--global-teardown ()
+  (with-demoted-errors "Error in ‘lsp--global-teardown’: %S"
+    (maphash (lambda (_k value) (lsp--teardown-workspace value)) lsp--workspaces)))
+
+(defun lsp--teardown-workspace (workspace)
+  (setq lsp--cur-workspace workspace)
+  (lsp--shutdown-cur-workspace))
+
+(defun lsp--shutdown-cur-workspace ()
+  "Shut down the language server process for ‘lsp--cur-workspace’."
+  (with-demoted-errors "LSP error: %S"
+    (lsp--send-request (lsp--make-request "shutdown" (make-hash-table)) t)
+    (lsp--send-notification (lsp--make-notification "exit" nil)))
+  (lsp--uninitialize-workspace))
+
+(defun lsp--uninitialize-workspace ()
+  "When a workspace is shut down, by request or from just
+disappearing, unset all the variables related to it."
+  (lsp-kill-watch (lsp--workspace-watches lsp--cur-workspace))
+
+  (let (proc
+        (root (lsp--workspace-root lsp--cur-workspace)))
+    (with-current-buffer (current-buffer)
+      (setq proc (lsp--workspace-proc lsp--cur-workspace))
+      (if (process-live-p proc)
+        (kill-process (lsp--workspace-proc lsp--cur-workspace)))
+      (setq lsp--cur-workspace nil)
+      (lsp--unset-variables)
+      (kill-local-variable 'lsp--cur-workspace))
+    (remhash root lsp--workspaces)))
+
+(defun lsp-restart-workspace ()
+  "Shut down and then restart the current workspace.
+This involves uninitializing each of the buffers associated with
+the workspace, closing the process managing communication with
+the client, and then starting up again."
+  (interactive)
+  (when (and (lsp-mode) (buffer-file-name) lsp--cur-workspace)
+    (let ((old-buffers (lsp--workspace-buffers lsp--cur-workspace))
+           (restart (lsp--client-enable-function (lsp--workspace-client lsp--cur-workspace)))
+           (proc (lsp--workspace-proc lsp--cur-workspace)))
+      (lsp--remove-cur-overlays)
+      ;; Shut down the LSP mode for each buffer in the workspace
+      (dolist (buffer old-buffers)
+        (with-current-buffer buffer
+          (lsp--text-document-did-close)
+          (setq lsp--cur-workspace nil)
+          (lsp-mode -1)))
+
+      ;; Let the process actually shut down
+      (while (process-live-p proc)
+        (accept-process-output proc))
+
+      ;; Re-enable LSP mode for each buffer
+      (dolist (buffer old-buffers)
+        (with-current-buffer buffer
+          (funcall restart))))))
+
+;; NOTE: Possibly make this function subject to a setting, if older LSP servers
+;; are unhappy
+(defun lsp--client-capabilities ()
+  "Return the client capabilites."
+  (apply #'lsp--merge-plists
+    `(:workspace    ,(lsp--client-workspace-capabilities)
+       :textDocument ,(lsp--client-textdocument-capabilities))
+    (seq-map (lambda (extra-capabilities-cons)
+               (let* ((package-name (car extra-capabilities-cons))
+                       (value (cdr extra-capabilities-cons))
+                       (capabilities (if (functionp value) (funcall value)
+                                       value)))
+                 (if (and capabilities (not (listp capabilities)))
+                   (progn
+                     (message "Capabilities provided by %s are not a plist: %s" package-name value)
+                     nil)
+                   capabilities)))
+      (lsp--workspace-extra-client-capabilities lsp--cur-workspace))))
+
+(defun lsp--merge-plists (first &rest rest)
+  "Deeply merge plists.
+
+FIRST is the plist to be merged into. The rest of the arguments
+can be either plists or nil. The non-nil plists in the rest of
+the arguments will be merged into FIRST.
+
+Return the merged plist."
+  (cl-check-type first list)
+  (seq-each
+    (lambda (pl) (setq first (lsp--merge-two-plists first pl)))
+    rest)
+  first)
+
+(defun lsp--merge-two-plists (first second)
+  "Deeply merge two plists.
+
+All values in SECOND are merged into FIRST.  FIRST can be nil or
+a plist.  SECOND must be a plist.
+
+Return the merged plist."
+  (when second
+    (if (not (listp second))
+      (warn "Cannot merge non-list value into a plist. The value is %s" second)
+      (cl-loop for (key second-value) on second
+        collect (progn
+                  (let ((first-value (plist-get first key))
+                         merged-value)
+                    (cond
+                      ((null second-value)) ; do nothing
+                      ((null first-value)
+                        (if (listp second-value)
+                          ;; Deep copy second-value so that the original value won't
+                          ;; be modified.
+                          (setq merged-value
+                            (lsp--merge-two-plists nil second-value)))
+                        (setq merged-value second-value))
+                      ((and (listp first-value) (listp second-value))
+                        (setq merged-value (lsp--merge-two-plists first-value second-value)))
+                      ;; Otherwise, the first value is a leaf entry and should
+                      ;; not be overridden.
+                      )
+                    (when merged-value
+                      (setq first (plist-put first key merged-value))))))))
+  first)
+
+(defun lsp--server-register-capability (reg)
+  (lsp--cur-workspace-check)
+  (let ((method (gethash "method" reg)))
+    (push
+      (make-lsp--registered-capability
+        :id (gethash "id" reg)
+        :method method
+        :options (gethash "registerOptions" reg))
+      (lsp--workspace-registered-server-capabilities lsp--cur-workspace))))
+
+(defun lsp--server-unregister-capability (unreg)
+  (let* ((id (gethash "id" unreg))
+          (fn (lambda (e) (equal (lsp--registered-capability-id e) id))))
+    (setf (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
+      (seq-remove fn
+        (lsp--workspace-registered-server-capabilities lsp--cur-workspace)))))
+
+(defun lsp--client-workspace-capabilities ()
+  "Client Workspace capabilities according to LSP."
+  `(:applyEdit t
+     :executeCommand (:dynamicRegistration t)))
+
+(defun lsp--client-textdocument-capabilities ()
+  "Client Text document capabilities according to LSP."
+  `(:synchronization (:willSave t :didSave t :willSaveWaitUntil t)
+                     :symbol (:symbolKind (:valueSet ,(cl-coerce (cl-loop for kind from 1 to 25 collect kind) 'vector)))
+                     :formatting (:dynamicRegistration t)
+                     :codeAction (:dynamicRegistration t)))
+
+(defun lsp-register-client-capabilities (package-name caps)
+  "Register extra client capabilities for the current workspace.
+
+This function must be called before the initialize request is
+sent.  It's recommended to to call it in the
+`lsp-before-initialize-hook'.
+
+PACKAGE name is the symbol of the name of the package that
+registers the capabilities.  CAPS is either a plist of the
+capabilities, or a function that takes no argument and return a
+plist of the client capabilties or nil.
+
+Registered capabilities are merged into the default capabilities
+before sending to the server via the initialize request.  If two
+packages provide different values for the same leaf capability
+entry, the value is set to the one that registers later.  Default
+leaf capability entries can not be overwritten."
+  (lsp--cur-workspace-check)
+  (cl-check-type package-name symbolp)
+  (cl-check-type caps (or list function))
+  (let ((extra-client-capabilities
+          (lsp--workspace-extra-client-capabilities lsp--cur-workspace)))
+    (if (alist-get package-name extra-client-capabilities)
+        (message "%s has already registered client capabilities" package-name)
+      (push `(,package-name . ,caps)
+        (lsp--workspace-extra-client-capabilities lsp--cur-workspace)))))
+
+(defun lsp-unregister-client-capabilities (package-name)
+  "Unregister extra capabilities provided by PACKAGE-NAME for the current workspace.
+
+PACKAGE-NAME is a symbol of the name of the package that has
+registered client capabilities by calling
+`lsp-register-client-capabilities'."
+  (lsp--cur-workspace-check)
+  (cl-check-type package-name symbol)
+  (let ((extra-client-capabilities
+          (lsp--workspace-extra-client-capabilities lsp--cur-workspace)))
+    (setf (lsp--workspace-extra-client-capabilities lsp--cur-workspace)
+      (assq-delete-all package-name extra-client-capabilities))))
+
+(define-inline lsp--server-capabilities ()
+  "Return the capabilities of the language server associated with the buffer."
+  (inline-quote (lsp--workspace-server-capabilities lsp--cur-workspace)))
+
+(defun lsp--server-has-sync-options-p ()
+  "Return whether the server has a TextDocumentSyncOptions object in
+ServerCapabilities.textDocumentSync."
+  (hash-table-p (gethash "textDocumentSync" (lsp--server-capabilities))))
+
+(defun lsp--send-open-close-p ()
+  "Return whether open and close notifications should be sent to the server."
+  (let ((sync (gethash "textDocumentSync" (lsp--server-capabilities))))
+    (and (hash-table-p sync)
+      (gethash "openClose" sync))))
+
+(defun lsp--send-will-save-p ()
+  "Return whether will save notifications should be sent to the server."
+  (let ((sync (gethash "textDocumentSync" (lsp--server-capabilities))))
+    (and (hash-table-p sync)
+      (gethash "willSave" sync))))
+
+(defun lsp--send-will-save-wait-until-p ()
+  "Return whether will save wait until notifications should be sent to the server."
+  (let ((sync (gethash "textDocumentSync" (lsp--server-capabilities))))
+      (and (hash-table-p sync)
+        (gethash "willSaveWaitUntil" sync))))
+
+(defun lsp--save-include-text-p ()
+  "Return whether save notifications should include the text document's contents."
+  (let ((sync (gethash "textDocumentSync" (lsp--server-capabilities))))
+    (and (hash-table-p sync)
+      (hash-table-p (gethash "save" sync nil))
+      (gethash "includeText" (gethash "save" sync)))))
+
+(defun lsp--set-sync-method ()
+  (let* ((sync (gethash "textDocumentSync" (lsp--server-capabilities)))
+          (kind (if (hash-table-p sync) (gethash "change" sync) sync))
+          (method (alist-get kind lsp--sync-methods)))
+    (setq lsp--server-sync-method (or lsp-document-sync-method
+                                    method))))
+
+(defun lsp--workspace-apply-edit-handler (_workspace params)
+  (lsp--apply-workspace-edit (gethash "edit" params)))
+
+(defun lsp--make-sentinel (workspace)
+  (cl-check-type workspace lsp--workspace)
+  (lambda (process exit-str)
+    (let ((status (process-status process)))
+      (when (memq status '(exit signal))
+        ;; Server has exited.  Uninitialize all buffer-local state for this
+        ;; workspace.
+        (message "%s: %s has exited (%s)"
+                 (lsp--workspace-root workspace)
+                 (process-name (lsp--workspace-proc workspace))
+                 (string-trim-right exit-str))
+        (dolist (buf (lsp--workspace-buffers workspace))
+          (with-current-buffer buf
+            (lsp--uninitialize-workspace)))
+        ;; Kill standard error buffer only if the process exited normally.
+        ;; Leave it intact otherwise for debugging purposes.
+        (when (and (eq status 'exit) (zerop (process-exit-status process)))
+          ;; FIXME: The client structure should store the standard error
+          ;; buffer, not its name.
+          ;; FIXME: Probably the standard error buffer should be per workspace,
+          ;; not per client.
+          (let ((stderr (get-buffer (lsp--client-stderr
+                                     (lsp--workspace-client workspace)))))
+            (when (buffer-live-p stderr)
+              (kill-buffer stderr))))))))
+
+(defun lsp--should-start-p (root)
+  "Consult `lsp-project-blacklist' and `lsp-project-whitelist' to
+determine if a server should be started for the given ROOT
+directory."
+  (or
+    (cl-some (lambda (p) (string-match-p p root))
+      lsp-project-whitelist)
+    (cl-notany (lambda (p) (string-match-p p root))
+      lsp-project-blacklist)))
+
+(defun lsp--start (client &optional extra-init-params)
+  (when lsp--cur-workspace
+    (user-error "LSP mode is already enabled for this buffer"))
+  (cl-assert client)
+  (let* ((root (file-truename (funcall (lsp--client-get-root client))))
+         (workspace (gethash root lsp--workspaces))
+         new-conn response init-params
+         parser proc cmd-proc)
+    (if workspace
+        (progn
+          (setq lsp--cur-workspace workspace)
+          (lsp-mode 1))
+
+      (setf
+       parser (make-lsp--parser)
+       lsp--cur-workspace (make-lsp--workspace
+                           :parser parser
+                           :file-versions (make-hash-table :test 'equal)
+                           :root root
+                           :client client)
+       (lsp--parser-workspace parser) lsp--cur-workspace
+       new-conn (funcall
+                 (lsp--client-new-connection client)
+                 (lsp--parser-make-filter parser (lsp--client-ignore-regexps client))
+                 (lsp--make-sentinel lsp--cur-workspace))
+       ;; the command line process invoked
+       cmd-proc (if (consp new-conn) (car new-conn) new-conn)
+       ;; the process we actually communicate with
+       proc (if (consp new-conn) (cdr new-conn) new-conn)
+
+       (lsp--workspace-proc lsp--cur-workspace) proc
+       (lsp--workspace-cmd-proc lsp--cur-workspace) cmd-proc)
+
+      (puthash root lsp--cur-workspace lsp--workspaces)
+      (lsp-mode 1)
+      (run-hooks 'lsp-before-initialize-hook)
+      (setq init-params
+            `(:processId ,(emacs-pid)
+                         :rootPath ,root
+                         :rootUri ,(lsp--path-to-uri root)
+                         :capabilities ,(lsp--client-capabilities)
+                         :initializationOptions ,(if (functionp extra-init-params)
+                                                     (funcall extra-init-params lsp--cur-workspace)
+                                                   extra-init-params)))
+      (setf response (lsp--send-request
+                      (lsp--make-request "initialize" init-params)))
+      (unless response
+        (signal 'lsp-empty-response-error (list "initialize")))
+      (setf (lsp--workspace-server-capabilities lsp--cur-workspace)
+            (gethash "capabilities" response))
+      ;; Version 3.0 now sends an "initialized" notification to allow registration
+      ;; of server capabilities
+      (lsp--send-notification (lsp--make-notification "initialized" (make-hash-table)))
+      (run-hooks 'lsp-after-initialize-hook))
+    (lsp--text-document-did-open)))
+
+(defun lsp--text-document-did-open ()
+  (run-hooks 'lsp-before-open-hook)
+  (puthash (current-buffer) 0 (lsp--workspace-file-versions lsp--cur-workspace))
+  (push (current-buffer) (lsp--workspace-buffers lsp--cur-workspace))
+  (lsp--send-notification (lsp--make-notification
+                           "textDocument/didOpen"
+                           `(:textDocument ,(lsp--make-text-document-item))))
+
+  (add-hook 'after-save-hook #'lsp-on-save nil t)
+  (add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)
+
+  (when lsp-enable-eldoc
+    ;; XXX: The documentation for `eldoc-documentation-function' suggests
+    ;; using `add-function' for modifying its value, use that instead?
+    (setq-local eldoc-documentation-function #'lsp--on-hover)
+    (eldoc-mode 1))
+
+  (when (and lsp-enable-indentation
+             (lsp--capability "documentRangeFormattingProvider"))
+    (setq-local indent-region-function #'lsp-format-region))
+
+  (when (and lsp-enable-xref
+             (lsp--capability "referencesProvider")
+             (lsp--capability "definitionProvider"))
+    (setq-local xref-backend-functions (list #'lsp--xref-backend)))
+
+  (when (and lsp-enable-completion-at-point (lsp--capability "completionProvider"))
+    (setq-local completion-at-point-functions nil)
+    (add-hook 'completion-at-point-functions #'lsp-completion-at-point nil t))
+
+  ;; Make sure the hook is local (last param) otherwise we see all changes for all buffers
+  (add-hook 'before-change-functions #'lsp-before-change nil t)
+  (add-hook 'after-change-functions #'lsp-on-change nil t)
+  (add-hook 'after-revert-hook #'lsp-on-revert nil t)
+  (add-hook 'before-save-hook #'lsp--before-save nil t)
+  (add-hook 'auto-save-hook #'lsp--on-auto-save nil t)
+  (lsp--set-sync-method)
+  (run-hooks 'lsp-after-open-hook))
+
+(define-inline lsp--text-document-identifier ()
+  "Make TextDocumentIdentifier.
+
+interface TextDocumentIdentifier {
+    uri: string;
+}"
+  (inline-quote (list :uri (lsp--buffer-uri))))
+
+(define-inline lsp--versioned-text-document-identifier ()
+  "Make VersionedTextDocumentIdentifier.
+
+interface VersionedTextDocumentIdentifier extends TextDocumentIdentifier {
+    version: number;
+}"
+  (inline-quote (plist-put (lsp--text-document-identifier)
+                  :version (lsp--cur-file-version))))
+
+(define-inline lsp--position (line char)
+  "Make a Position object for the given LINE and CHAR.
+
+interface Position {
+    line: number;
+    character: number;
+}"
+  (inline-quote (list :line ,line :character ,char)))
+
+(define-inline lsp--cur-line ()
+  (inline-quote (1- (line-number-at-pos))))
+
+(define-inline lsp--cur-column ()
+  (inline-quote (- (point) (line-beginning-position))))
+
+(define-inline lsp--cur-position ()
+  "Make a Position object for the current point."
+  (inline-quote
+   (save-restriction
+     (widen)
+     (lsp--position (lsp--cur-line) (lsp--cur-column)))))
+
+(defun lsp--point-to-position (point)
+  "Convert POINT to Position."
+  (save-excursion
+    (goto-char point)
+    (lsp--cur-position)))
+
+(define-inline lsp--position-p (p)
+  (inline-quote
+    (and (numberp (plist-get ,p :line)) (numberp (plist-get ,p :character)))))
+
+(define-inline lsp--range (start end)
+  "Make Range body from START and END.
+
+interface Range {
+     start: Position;
+     end: Position;
+ }"
+  ;; make sure start and end are Position objects
+  (inline-quote
+    (progn
+      (cl-check-type ,start (satisfies lsp--position-p))
+      (cl-check-type ,end (satisfies lsp--position-p))
+      (list :start ,start :end ,end))))
+
+(define-inline lsp--region-to-range (start end)
+  "Make Range object for the current region."
+  (inline-quote (lsp--range (lsp--point-to-position ,start)
+                  (lsp--point-to-position ,end))))
+
+(defun lsp--current-region-or-pos ()
+  "If the region is active return that, else get the point."
+  (if (use-region-p)
+      (lsp--region-to-range (region-beginning) (region-end))
+    (lsp--region-to-range (point) (point))))
+
+(defun lsp--get-start-position ()
+  "Get the start of the region if active, else current point."
+  (let ((pos (if (use-region-p)
+                 (region-beginning)
+               (point))))
+    (lsp-point-to-position pos)))
+
+(defun lsp--get-end-position ()
+  "Get the end of the region if active, else current point."
+  (let ((pos (if (use-region-p)
+                 (region-end)
+               (point))))
+    (lsp-point-to-position pos)))
+
+(define-inline lsp--range-start-line (range)
+  "Return the start line for a given LSP range, in LSP coordinates"
+  (inline-quote (plist-get (plist-get ,range :start) :line)))
+
+(define-inline lsp--range-end-line (range)
+  "Return the end line for a given LSP range, in LSP coordinates"
+  (inline-quote (plist-get (plist-get ,range :end) :line)))
+
+(defun lsp--apply-workspace-edit (edit)
+  "Apply the WorkspaceEdit object EDIT.
+
+interface WorkspaceEdit {
+  changes?: { [uri: string]: TextEdit[]; };
+  documentChanges?: TextDocumentEdit[];
+}"
+  (let ((changes (gethash "changes" edit))
+         (document-changes (gethash "documentChanges" edit)))
+    (if document-changes
+      (seq-do #'lsp--apply-text-document-edit document-changes)
+
+      (when (hash-table-p changes)
+        (maphash
+          (lambda (uri text-edits)
+            (let ((filename (lsp--uri-to-path uri)))
+              (with-current-buffer (find-file-noselect filename)
+                (lsp--apply-text-edits text-edits))))
+          changes)))))
+
+(defun lsp--apply-text-document-edit (edit)
+  "Apply the TextDocumentEdit object EDIT.
+If the file is not being visited by any buffer, it is opened with
+`find-file-noselect'.
+Because lsp-mode does not store previous document versions, the edit is only
+applied if the version of the textDocument matches the version of the
+corresponding file.
+
+interface TextDocumentEdit {
+  textDocument: VersionedTextDocumentIdentifier;
+  edits: TextEdit[];
+}"
+  (let* ((ident (gethash "textDocument" edit))
+          (filename (lsp--uri-to-path (gethash "uri" ident)))
+          (version (gethash "version" ident)))
+    (with-current-buffer (find-file-noselect filename)
+      (when (and version (= version (lsp--cur-file-version)))
+        (lsp--apply-text-edits (gethash "edits" edit))))))
+
+(defun lsp--text-edit-sort-predicate (e1 e2)
+  (let ((start1 (lsp--position-to-point (gethash "start" (gethash "range" e1))))
+          (start2 (lsp--position-to-point (gethash "start" (gethash "range" e2)))))
+    (if (= start1 start2)
+      (let ((end1 (lsp--position-to-point (gethash "end" (gethash "range" e1))))
+             (end2 (lsp--position-to-point (gethash "end" (gethash "range" e2)))))
+        (> end1 end2))
+
+      (> start1 start2))))
+
+(define-inline lsp--apply-text-edits (edits)
+  "Apply the edits described in the TextEdit[] object in EDITS."
+  (inline-quote
+    ;; We sort text edits so as to apply edits that modify earlier parts of the
+    ;; document first. Furthermore, because the LSP spec dictates that:
+    ;; "If multiple inserts have the same position, the order in the array
+    ;; defines which edit to apply first."
+    ;; We reverse the initial list to make sure that the order among edits with
+    ;; the same position is preserved.
+
+    (seq-do #'lsp--apply-text-edit (sort (nreverse ,edits) #'lsp--text-edit-sort-predicate))))
+
+(defun lsp--apply-text-edit (text-edit)
+  "Apply the edits described in the TextEdit object in TEXT-EDIT."
+  (let* ((range (gethash "range" text-edit))
+         (start-point (lsp--position-to-point (gethash "start" range)))
+         (end-point (lsp--position-to-point (gethash "end" range))))
+    (save-excursion
+      (goto-char start-point)
+      (delete-region start-point end-point)
+      (insert (gethash "newText" text-edit)))))
+
+(define-inline lsp--capability (cap &optional capabilities)
+  "Get the value of capability CAP.  If CAPABILITIES is non-nil, use them instead."
+  (inline-quote (gethash ,cap (or ,capabilities (lsp--server-capabilities) (make-hash-table)))))
+
+(define-inline lsp--registered-capability (method)
+  (inline-quote
+   (seq-find (lambda (reg) (equal (lsp--registered-capability-method reg) ,method))
+             (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
+             nil)))
+
+(define-inline lsp--registered-capability-by-id (id)
+  (inline-quote
+   (seq-find (lambda (reg) (equal (lsp--registered-capability-id reg) ,id))
+             (lsp--workspace-registered-server-capabilities lsp--cur-workspace)
+             nil)))
+
+(defvar-local lsp--before-change-vals nil
+  "Store the positions from the `lsp-before-change' function
+  call, for validation and use in the `lsp-on-change' function.")
+
+(defun lsp--text-document-content-change-event (start end length)
+  "Make a TextDocumentContentChangeEvent body for START to END, of length LENGTH."
+  ;; So (47 54 0) means add    7 chars starting at pos 47
+  ;; must become
+  ;;   {"range":{"start":{"line":5,"character":6}
+  ;;             ,"end" :{"line":5,"character":6}}
+  ;;             ,"rangeLength":0
+  ;;             ,"text":"\nbb = 5"}
+  ;;
+  ;; And (47 47 7) means delete 7 chars starting at pos 47
+  ;; must become
+  ;;   {"range":{"start":{"line":6,"character":0}
+  ;;            ,"end"  :{"line":7,"character":0}}
+  ;;            ,"rangeLength":7
+  ;;            ,"text":""}
+  ;;
+  ;; (208 221 3) means delete 3 chars starting at pos 208, and replace them with
+  ;; 13 chars. So it must become
+  ;;   {"range":{"start":{"line":5,"character":8}
+  ;;             ,"end" :{"line":5,"character":11}}
+  ;;             ,"rangeLength":3
+  ;;             ,"text":"new-chars-xxx"}
+  ;;
+
+  ;; Adding text:
+  ;;   lsp-before-change:(start,end)=(33,33)
+  ;;   lsp-on-change:(start,end,length)=(33,34,0)
+  ;;
+  ;; Changing text:
+  ;;   lsp-before-change:(start,end)=(208,211)
+  ;;   lsp-on-change:(start,end,length)=(208,221,3)
+  ;;
+  ;; Deleting text:
+  ;;   lsp-before-change:(start,end)=(19,27)
+  ;;   lsp-on-change:(start,end,length)=(19,19,8)
+
+  (if (eq length 0)
+    ;; Adding something only, work from start only
+    `(:range ,(lsp--range (lsp--point-to-position start)
+                (lsp--point-to-position start))
+       :rangeLength 0
+       :text ,(buffer-substring-no-properties start end))
+
+    (if (eq start end)
+      ;; Deleting something only
+      (if (lsp--bracketed-change-p start end length)
+        ;; The before-change value is bracketed, use it
+        `(:range ,(lsp--range (lsp--point-to-position start)
+                    (plist-get lsp--before-change-vals :end-pos))
+           :rangeLength ,length
+           :text "")
+        ;; If the change is not bracketed, send a full change event instead.
+        (lsp--full-change-event))
+
+      ;; Deleting some things, adding others
+      (if (lsp--bracketed-change-p start end length)
+        ;; The before-change value is valid, use it
+        `(:range ,(lsp--range (lsp--point-to-position start)
+                    (plist-get lsp--before-change-vals :end-pos))
+           :rangeLength ,length
+           :text ,(buffer-substring-no-properties start end))
+        (lsp--full-change-event)))))
+
+
+;; TODO: Add tests for this function.
+(defun lsp--bracketed-change-p (start _end length)
+  "If the before and after positions are the same, and the length
+is the size of the start range, we are probably good."
+  (and (eq start (plist-get lsp--before-change-vals :start) )
+       (eq length (- (plist-get lsp--before-change-vals :end)
+                     (plist-get lsp--before-change-vals :start)))))
+
+;; Observed from vscode for applying a diff replacing one line with
+;; another. Emacs on-change shows this as a delete followed by an
+;; add.
+
+;; 2017-04-22 17:43:59 [ThreadId 11] DEBUG haskell-lsp - ---> {"jsonrpc":"2.0","method":"textDocument/didChange","params":
+;; {"textDocument":{"uri":"file:///home/alanz/tmp/haskell-hie-test-project/src/Foo.hs","version":2}
+;; ,"contentChanges":[{"range":{"start":{"line":7,"character":0}
+;;                             ,"end":  {"line":7,"character":8}}
+;;                     ,"rangeLength":8
+;;                     ,"text":"baz ="}]}}
+
+
+(defun lsp--full-change-event ()
+  (save-restriction
+    (widen)
+    `(:text ,(buffer-substring-no-properties (point-min) (point-max)))))
+
+(defun lsp-before-change (start end)
+  "Executed before a file is changed.
+Added to `before-change-functions'."
+  ;; Note:
+  ;;
+  ;; This variable holds a list of functions to call when Emacs is about to
+  ;; modify a buffer. Each function gets two arguments, the beginning and end of
+  ;; the region that is about to change, represented as integers. The buffer
+  ;; that is about to change is always the current buffer when the function is
+  ;; called.
+  ;;
+  ;; WARNING:
+  ;;
+  ;; Do not expect the before-change hooks and the after-change hooks be called
+  ;; in balanced pairs around each buffer change. Also don't expect the
+  ;; before-change hooks to be called for every chunk of text Emacs is about to
+  ;; delete. These hooks are provided on the assumption that Lisp programs will
+  ;; use either before- or the after-change hooks, but not both, and the
+  ;; boundaries of the region where the changes happen might include more than
+  ;; just the actual changed text, or even lump together several changes done
+  ;; piecemeal.
+  ;; (message "lsp-before-change:(start,end)=(%s,%s)" start end)
+  (with-demoted-errors "Error in ‘lsp-before-change’: %S"
+    (setq lsp--before-change-vals
+          (list :start start
+                :end end
+                :start-pos (lsp--point-to-position start)
+                :end-pos (lsp--point-to-position end)))))
+
+(defun lsp-on-change (start end length)
+  "Executed when a file is changed.
+Added to `after-change-functions'."
+  ;; Note:
+  ;;
+  ;; Each function receives three arguments: the beginning and end of the region
+  ;; just changed, and the length of the text that existed before the change.
+  ;; All three arguments are integers. The buffer that has been changed is
+  ;; always the current buffer when the function is called.
+  ;;
+  ;; The length of the old text is the difference between the buffer positions
+  ;; before and after that text as it was before the change. As for the
+  ;; changed text, its length is simply the difference between the first two
+  ;; arguments.
+  ;;
+  ;; So (47 54 0) means add    7 chars starting at pos 47
+  ;; So (47 47 7) means delete 7 chars starting at pos 47
+  ;; (message "lsp-on-change:(start,end,length)=(%s,%s,%s)" start end length)
+  ;; (message "lsp-on-change:(lsp--before-change-vals)=%s" lsp--before-change-vals)
+  (with-demoted-errors "Error in ‘lsp-on-change’: %S"
+    (save-match-data
+      ;; A (revert-buffer) call with the 'preserve-modes parameter (eg, as done
+      ;; by auto-revert-mode) will cause this hander to get called with a nil
+      ;; buffer-file-name. We need the buffer-file-name to send notifications;
+      ;; so we skip handling revert-buffer-caused changes and instead handle
+      ;; reverts separately in lsp-on-revert
+      (when (and lsp--cur-workspace (not revert-buffer-in-progress-p))
+        (lsp--inc-cur-file-version)
+        (unless (eq lsp--server-sync-method 'none)
+          (lsp--send-notification
+           (lsp--make-notification
+            "textDocument/didChange"
+            `(:textDocument
+              ,(lsp--versioned-text-document-identifier)
+              :contentChanges
+              ,(pcase lsp--server-sync-method
+                 ('incremental (vector (lsp--text-document-content-change-event
+                                        start end length)))
+                 ('full (vector (lsp--full-change-event))))))))))))
+
+(defun lsp-on-revert ()
+  "Executed when a file is reverted.
+Added to `after-revert-hook'."
+  (let ((n (buffer-size))
+        (revert-buffer-in-progress-p nil))
+    (lsp-on-change 0 n n)))
+
+(defun lsp--text-document-did-close ()
+  "Executed when the file is closed, added to `kill-buffer-hook'."
+  (when lsp--cur-workspace
+    (with-demoted-errors "Error on ‘lsp--text-document-did-close’: %S"
+      (let ((file-versions (lsp--workspace-file-versions lsp--cur-workspace))
+            (old-buffers (lsp--workspace-buffers lsp--cur-workspace)))
+        ;; remove buffer from the current workspace's list of buffers
+        ;; do a sanity check first
+        (when (memq (current-buffer) old-buffers)
+          (setf (lsp--workspace-buffers lsp--cur-workspace)
+                (delq (current-buffer) old-buffers))
+
+          (remhash (current-buffer) file-versions)
+          (with-demoted-errors "Error sending didClose notification in ‘lsp--text-document-did-close’: %S"
+            (lsp--send-notification
+             (lsp--make-notification
+              "textDocument/didClose"
+              `(:textDocument ,(lsp--versioned-text-document-identifier)))))
+          (when (= 0 (hash-table-count file-versions))
+            (lsp--shutdown-cur-workspace)))))))
+
+(define-inline lsp--will-save-text-document-params (reason)
+  (cl-check-type reason number)
+  (inline-quote
+    (list :textDocument (lsp--text-document-identifier)
+      :reason ,reason)))
+
+(defun lsp--before-save ()
+  (when lsp--cur-workspace
+    (with-demoted-errors "Error in ‘lsp--before-save’: %S"
+      (let ((params (lsp--will-save-text-document-params 1)))
+        (when (lsp--send-will-save-p)
+          (lsp--send-notification
+            (lsp--make-notification "textDocument/willSave" params)))
+        (when (and (lsp--send-will-save-wait-until-p) lsp-before-save-edits)
+          (lsp--apply-text-edits
+           (lsp--send-request (lsp--make-request
+                               "textDocument/willSaveWaitUntil" params))))))))
+
+(defun lsp--on-auto-save ()
+  (when (and lsp--cur-workspace
+          (lsp--send-will-save-p))
+    (with-demoted-errors "Error in ‘lsp--on-auto-save’: %S"
+      (lsp--send-notification
+        (lsp--make-notification
+          "textDocument/willSave" (lsp--will-save-text-document-params 2))))))
+
+(defun lsp--text-document-did-save ()
+  "Executed when the file is closed, added to `after-save-hook''."
+  (when lsp--cur-workspace
+    (with-demoted-errors "Error on ‘lsp--text-document-did-save: %S’"
+      (lsp--send-notification
+       (lsp--make-notification
+        "textDocument/didSave"
+         `(:textDocument ,(lsp--versioned-text-document-identifier)
+                         :text ,(if (lsp--save-include-text-p)
+                                    (save-excursion
+                                      (widen)
+                                      (buffer-substring-no-properties (point-min) (point-max)))
+                                  nil)))))))
+
+(define-inline lsp--text-document-position-params (&optional identifier position)
+  "Make TextDocumentPositionParams for the current point in the current document.
+If IDENTIFIER and POSITION are non-nil, they will be used as the document identifier
+and the position respectively."
+  (inline-quote (list :textDocument (or ,identifier (lsp--text-document-identifier))
+                      :position (or ,position (lsp--cur-position)))))
+
+(define-inline lsp--text-document-code-action-params ()
+  "Make CodeActionParams for the current region in the current document."
+  (inline-quote (list :textDocument (lsp--text-document-identifier)
+                  :range (lsp--current-region-or-pos)
+                  :context (list :diagnostics (lsp--cur-line-diagnotics)))))
+
+(defun lsp--cur-line-diagnotics ()
+  "Return any diagnostics that apply to the current line."
+  (let* ((diags (gethash buffer-file-name lsp--diagnostics nil))
+         (range (lsp--current-region-or-pos))
+         (start-line (lsp--range-start-line range))
+         (end-line (lsp--range-end-line range))
+         (diags-in-range (cl-remove-if-not
+                          (lambda (diag)
+                            (let ((line (lsp-diagnostic-line diag)))
+                              (and (>= line start-line) (<= line end-line))))
+                          diags)))
+    (cl-coerce (seq-map #'lsp-diagnostic-original diags-in-range) 'vector)))
+
+(defconst lsp--completion-item-kind
+  [nil
+   "Text"
+   "Method"
+   "Function"
+   "Constructor"
+   "Field"
+   "Variable"
+   "Class"
+   "Interface"
+   "Module"
+   "Property"
+   "Unit"
+   "Value"
+   "Enum"
+   "Keyword"
+   "Snippet"
+   "Color"
+   "File"
+   "Reference"
+   "Folder"
+   "EnumMember"
+   "Constant"
+   "Struct"
+   "Event"
+   "Operator"
+   "TypeParameter"
+   ])
+
+(defun lsp--gethash (key table &optional dflt)
+  "Look up KEY in TABLE and return its associated value,
+unless KEY not found or its value is falsy, when it returns DFLT.
+DFLT defaults to nil.
+
+Needed for completion request fallback behavior for the fields
+'sortText', 'filterText', and 'insertText' as described here:
+
+https://microsoft.github.io/language-server-protocol/specification#textDocument_completion"
+
+  (let ((result (gethash key table dflt)))
+    (when (member result '(nil "" 0 :json-false))
+      (setq result dflt))
+    result))
+
+(defun lsp--make-completion-item (item)
+  (propertize (lsp--gethash "insertText" item (gethash "label" item ""))
+              'lsp-completion-item
+              item))
+
+(defun lsp--annotate (item)
+  (let* ((table (plist-get (text-properties-at 0 item) 'lsp-completion-item))
+         (detail (gethash "detail" table nil))
+         (kind-index (gethash "kind" table nil)))
+    ;; We need check index before call `aref'.
+    (when kind-index
+      (setq kind (aref lsp--completion-item-kind kind-index))
+      (concat
+       " "
+       detail
+       (when kind (format " (%s)" kind))))
+    ))
+
+(defun lsp--sort-string (c)
+  (lsp--gethash "sortText" c (gethash "label" c "")))
+
+(defun lsp--sort-completions (completions)
+  (sort completions (lambda (c1 c2)
+                      (string-lessp
+                        (lsp--sort-string c1)
+                        (lsp--sort-string c2)))))
+
+(defun lsp--default-prefix-function ()
+  (bounds-of-thing-at-point 'symbol))
+
+(defun lsp--get-completions ()
+  (with-demoted-errors "Error in ‘lsp--get-completions’: %S"
+    (let* ((prefix-function (or (lsp--client-prefix-function
+                                  (lsp--workspace-client lsp--cur-workspace))
+                             #'lsp--default-prefix-function))
+            (bounds (funcall prefix-function)))
+      (list
+       (if bounds (car bounds) (point))
+       (if bounds (cdr bounds) (point))
+       (completion-table-dynamic
+        #'(lambda (_)
+            ;; *we* don't need to know the string being completed
+            ;; the language server does all the work by itself
+            (let* ((resp (lsp--send-request
+                          (lsp--make-request
+                           "textDocument/completion"
+                           (lsp--text-document-position-params))))
+                   (items (cond
+                           ((null resp) nil)
+                           ((hash-table-p resp) (gethash "items" resp nil))
+                           ((sequencep resp) resp))))
+              (seq-map #'lsp--make-completion-item items))))
+       :annotation-function #'lsp--annotate
+       :display-sort-function #'lsp--sort-completions))))
+
+(defun lsp--resolve-completion (item)
+  (lsp--cur-workspace-check)
+  (cl-assert item nil "Completion item must not be nil")
+  (if (gethash "resolveProvider" (lsp--capability "completionProvider"))
+    (lsp--send-request
+      (lsp--make-request
+        "completionItem/resolve"
+        item))
+    item))
+
+(defun lsp--extract-line-from-buffer (pos)
+  "Return the line pointed to by POS (a Position object) in the current buffer."
+  (let* ((point (lsp--position-to-point pos))
+         (inhibit-field-text-motion t))
+    (save-excursion
+      (goto-char point)
+      (buffer-substring-no-properties (line-beginning-position)
+                                      (line-end-position)))))
+
+(defun lsp--xref-make-item (filename location)
+  "Return a xref-item from a LOCATION in FILENAME."
+  (let* ((range (gethash "range" location))
+         (pos-start (gethash "start" range))
+         (pos-end (gethash "end" range))
+         (line (lsp--extract-line-from-buffer pos-start))
+         (start (gethash "character" pos-start))
+         (end (gethash "character" pos-end))
+         (len (length line)))
+    (add-face-text-property (max (min start len) 0)
+                            (max (min end len) 0)
+                            'highlight t line)
+    ;; LINE is nil when FILENAME is not being current visited by any buffer.
+    (xref-make (or line filename)
+               (xref-make-file-location filename
+                                        (1+ (gethash "line" pos-start))
+                                        (gethash "character" pos-start)))))
+
+(defun lsp--get-xrefs-in-file (file)
+  "Return all references that contain a file.
+FILE is a cons where its car is the filename and the cdr is a list of Locations
+within the file.  We open and/or create the file/buffer only once for all
+references.  The function returns a list of `xref-item'."
+  (let* ((filename (car file))
+         (visiting (find-buffer-visiting filename))
+         (fn (lambda (loc) (lsp--xref-make-item filename loc))))
+    (if visiting
+        (with-current-buffer visiting
+          (mapcar fn (cdr file)))
+      (when (file-readable-p filename)
+        (with-temp-buffer
+          (insert-file-contents-literally filename)
+          (mapcar fn (cdr file)))))))
+
+(defun lsp--locations-to-xref-items (locations)
+  "Return a list of `xref-item' from LOCATIONS.
+LOCATIONS is an array of Location objects:
+
+interface Location {
+  uri: DocumentUri;
+  range: Range;
+}"
+  (when locations
+    (let* ((fn (lambda (loc) (lsp--uri-to-path (gethash "uri" loc))))
+            ;; locations-by-file is an alist of the form
+            ;; ((FILENAME . LOCATIONS)...), where FILENAME is a string of the
+            ;; actual file name, and LOCATIONS is a list of Location objects
+            ;; pointing to Ranges inside that file.
+            (locations-by-file (seq-group-by fn locations))
+            ;; items-by-file is a list of list of xref-item
+            (items-by-file (mapcar #'lsp--get-xrefs-in-file locations-by-file)))
+      ;; flatten the list
+      (apply #'append items-by-file))))
+
+(defun lsp--get-definitions ()
+  "Get definition of the current symbol under point.
+Returns xref-item(s)."
+  (let ((defs (lsp--send-request (lsp--make-request
+                                  "textDocument/definition"
+                                   (lsp--text-document-position-params)))))
+    ;; textDocument/definition returns Location | Location[]
+    (lsp--locations-to-xref-items (if (listp defs) defs (list defs)))))
+
+(defun lsp--make-reference-params (&optional td-position include-declaration)
+  "Make a ReferenceParam object.
+If TD-POSITION is non-nil, use it as TextDocumentPositionParams object instead.
+If INCLUDE-DECLARATION is non-nil, request the server to include declarations."
+  (let ((json-false :json-false))
+    (plist-put (or td-position (lsp--text-document-position-params))
+      :context `(:includeDeclaration ,(or include-declaration json-false)))))
+
+(defun lsp--get-references ()
+  "Get all references for the symbol under point.
+Returns xref-item(s)."
+  (let ((refs  (lsp--send-request (lsp--make-request
+                                   "textDocument/references"
+                                   (lsp--make-reference-params)))))
+    (lsp--locations-to-xref-items refs)))
+
+(defun lsp--cancel-request (id)
+  (lsp--cur-workspace-check)
+  (cl-check-type id (or number string))
+  (let ((response-handlers (lsp--client-response-handlers (lsp--workspace-client
+                                                            lsp--cur-workspace))))
+    (remhash id response-handlers)
+    (lsp--send-notification (lsp--make-notification "$/cancelRequest"
+                              `(:id ,id)))))
+
+(defun lsp--on-hover ()
+  ;; This function is used as ‘eldoc-documentation-function’, so it’s important
+  ;; that it doesn’t fail.
+  (with-demoted-errors "Error in ‘lsp--on-hover’: %S"
+    (when (and (lsp--capability "documentHighlightProvider")
+               lsp-highlight-symbol-at-point)
+      (lsp-symbol-highlight))
+    (when (and (or (lsp--capability "codeActionProvider")
+                   (lsp--registered-capability "textDocument/codeAction"))
+               lsp-enable-codeaction)
+      (lsp--text-document-code-action))
+    (when (and (lsp--capability "hoverProvider") lsp-enable-eldoc)
+      (funcall lsp-hover-text-function))))
+
+(defun lsp-describe-thing-at-point ()
+  "Display the full documentation of the thing at point."
+  (interactive)
+  (lsp--cur-workspace-check)
+  (let* ((client (lsp--workspace-client lsp--cur-workspace))
+         (contents (gethash "contents" (lsp--send-request
+                                        (lsp--make-request "textDocument/hover"
+                                                           (lsp--text-document-position-params))))))
+    (pop-to-buffer
+     (with-current-buffer (get-buffer-create "*lsp-help*")
+       (let ((inhibit-read-only t))
+         (erase-buffer)
+         (insert (lsp--render-on-hover-content contents client t))
+         (goto-char (point-min))
+         (view-mode t)
+         (current-buffer))))))
+
+(defvar-local lsp--cur-hover-request-id nil)
+
+(defun lsp--text-document-hover-string ()
+  "interface Hover {
+    contents: MarkedString | MarkedString[];
+    range?: Range;
+}
+
+type MarkedString = string | { language: string; value: string };"
+  (lsp--cur-workspace-check)
+  (when lsp--cur-hover-request-id
+    (lsp--cancel-request lsp--cur-hover-request-id))
+  (let* ((client (lsp--workspace-client lsp--cur-workspace))
+          bounds body)
+    (when (symbol-at-point)
+      (setq bounds (bounds-of-thing-at-point 'symbol)
+        body (lsp--send-request-async (lsp--make-request "textDocument/hover"
+                                        (lsp--text-document-position-params))
+               (lsp--make-hover-callback client (car bounds) (cdr bounds)
+                 (current-buffer)))
+        lsp--cur-hover-request-id (plist-get body :id))
+      (cl-assert (integerp lsp--cur-hover-request-id)))))
+
+(defun lsp--render-markup-content-1 (kind content)
+  (if (functionp lsp-render-markdown-markup-content)
+    (let ((out (funcall lsp-render-markdown-markup-content kind content)))
+      (cl-assert (stringp out) t
+        "value returned by lsp-render-markdown-markup-content should be a string")
+      out)
+    content))
+
+(defun lsp--render-markup-content (content)
+  "Render MarkupContent object CONTENT.
+
+export interface MarkupContent {
+        kind: MarkupKind;
+        value: string;
+}"
+  (let ((kind (gethash "kind" content))
+        (content (gethash "value" content)))
+    (lsp--render-markup-content-1 kind content)))
+
+(define-inline lsp--point-is-within-bounds-p (start end)
+  "Return whether the current point is within START and END."
+  (inline-quote
+    (let ((p (point)))
+      (and (>= p ,start) (<= p ,end)))))
+
+(define-inline lsp--markup-content-p (obj)
+  (inline-letevals (obj)
+    (inline-quote (and (hash-table-p ,obj)
+                    (gethash "kind" ,obj nil) (gethash "value" ,obj nil)))))
+
+(defun lsp--render-on-hover-content (contents client render-all)
+  "Render the content received from 'document/onHover' request.
+
+CLIENT - client to use.
+CONTENTS  - MarkedString | MarkedString[] | MarkupContent
+RENDER-ALL if set to nil render only the first element from CONTENTS."
+  (let ((renderers (lsp--client-string-renderers client))
+        (default-client-renderer (lsp--client-default-renderer client)))
+    (string-join
+     (seq-map
+      (lambda (e)
+        (let (renderer)
+          (cond
+           ;; hash table, language renderer set
+           ((and (hash-table-p e)
+                 (setq renderer
+                       (if-let (language (gethash "language" e))
+                           (cdr (assoc-string language renderers))
+                         default-client-renderer)))
+            (when (gethash "value" e nil)
+              (funcall renderer (gethash "value" e))))
+
+           ;; hash table - workspace renderer not set
+           ;; trying to render using global renderer
+           ((lsp--markup-content-p e) (lsp--render-markup-content e))
+
+           ;; hash table - anything other has failed
+           ((hash-table-p e) (gethash "value" e nil))
+
+           ;; string, default workspace renderer set
+           (default-client-renderer (funcall default-client-renderer  e))
+
+           ;; no rendering
+           (t e))))
+      (if (sequencep contents)
+          (if render-all
+              contents
+            (seq-take contents 1))
+        (list contents)))
+     "\n")))
+
+;; start and end are the bounds of the symbol at point
+(defun lsp--make-hover-callback (client start end buffer)
+  (lambda (hover)
+    (with-current-buffer buffer
+      (setq lsp--cur-hover-request-id nil))
+    (when (and hover
+               (lsp--point-is-within-bounds-p start end)
+               (eq (current-buffer) buffer) (eldoc-display-message-p))
+      (let ((contents (gethash "contents" hover)))
+        (when contents
+          (eldoc-message (lsp--render-on-hover-content contents
+                                                       client
+                                                       lsp-eldoc-render-all)))))))
+
+(defun lsp-provide-marked-string-renderer (client language renderer)
+  (cl-check-type language string)
+  (cl-check-type renderer function)
+  (setf (alist-get language (lsp--client-string-renderers client)) renderer))
+
+(defun lsp-provide-default-marked-string-renderer (client renderer)
+  "Set the RENDERER for CLIENT.
+
+It will be used when no language has been specified in document/onHover result."
+  (cl-check-type renderer function)
+  (setf (lsp--client-default-renderer client) renderer))
+
+(defun lsp-info-under-point ()
+  "Show relevant documentation for the thing under point."
+  (interactive)
+  (lsp--text-document-hover-string))
+
+(defvar-local lsp--current-signature-help-request-id nil)
+
+(defun lsp--text-document-signature-help ()
+  "interface SignatureHelp {
+signatures: SignatureInformation[];
+activeSignature?: number;
+activeParameter?: number;
+};
+
+interface SignatureInformation {
+label: string;
+documentation?: string | MarkupContent;
+parameters?: ParameterInformation[];
+};
+
+interface ParameterInformation {
+label: string;
+documentation?: string | MarkupContent;
+};
+
+interface MarkupContent {
+kind: MarkupKind;
+value: string;
+};
+
+type MarkupKind = 'plaintext' | 'markdown';"
+  (lsp--cur-workspace-check)
+  (when lsp--current-signature-help-request-id
+    (lsp--cancel-request lsp--current-signature-help-request-id))
+  (let (bounds body)
+    (when (symbol-at-point)
+      (setq bounds (bounds-of-thing-at-point 'symbol)
+            body (lsp--send-request-async
+                  (lsp--make-request "textDocument/signatureHelp"
+                                     (lsp--text-document-position-params))
+                  (lsp--make-text-document-signature-help-callback
+                   (car bounds) (cdr bounds) (current-buffer)))
+            lsp--current-signature-help-request-id (plist-get body :id))
+      (cl-assert (integerp lsp--current-signature-help-request-id)))))
+
+(defun lsp--make-text-document-signature-help-callback (start end buffer)
+  (lambda (signature-help)
+    (with-current-buffer buffer
+      (setq lsp--current-signature-help-request-id nil))
+    (when (and signature-help
+               (lsp--point-is-within-bounds-p start end)
+               (eq (current-buffer) buffer) (eldoc-display-message-p))
+      (let* ((active-signature-number
+              (or (gethash "activeSignature" signature-help) 0))
+             (active-signature (nth
+                                active-signature-number
+                                (gethash "signatures" signature-help))))
+        (when active-signature
+          (eldoc-message (gethash "label" active-signature)))))))
+
+;; NOTE: the code actions cannot currently be applied. There is some non-GNU
+;; code to do this in the lsp-haskell module. We still need a GNU version, here.
+;; PRs accepted.
+(defvar-local lsp-code-actions nil
+  "Code actions for the buffer.")
+
+(defvar-local lsp-code-action-params nil
+  "The last code action params.")
+
+(defun lsp--text-document-code-action ()
+  "Request code action to automatically fix issues reported by
+the diagnostics."
+  (lsp--cur-workspace-check)
+  (unless (or (lsp--capability "codeActionProvider")
+              (lsp--registered-capability "textDocument/codeAction"))
+    (signal 'lsp-capability-not-supported (list "codeActionProvider")))
+  (let ((params (lsp--text-document-code-action-params)))
+    (lsp--send-request-async
+     (lsp--make-request "textDocument/codeAction" params)
+     (lambda (actions)
+       (lsp--set-code-action-params (current-buffer) actions params)))))
+
+(defun lsp--command-get-title (cmd)
+  "Given a Command object CMD, get the title.
+If title is nil, return the name for the command handler."
+  (gethash "title" cmd (gethash "command" cmd)))
+
+(defun lsp--set-code-action-params (buf actions params)
+  "Update set `lsp-code-actions' to ACTIONS and `lsp-code-action-params' to PARAMS in BUF."
+  (when (buffer-live-p buf)
+    (with-current-buffer buf
+      (when (equal params (lsp--text-document-code-action-params))
+        (setq lsp-code-actions actions)
+        (setq lsp-code-action-params params)))))
+
+(defun lsp--command-p (cmd)
+  (and (cl-typep cmd 'hash-table)
+    (cl-typep (gethash "title" cmd) 'string)
+    (cl-typep (gethash "command" cmd) 'string)))
+
+(defun lsp--select-action (actions)
+  "Select an action to execute from ACTIONS."
+  (if actions
+      (let ((name->action (mapcar (lambda (a)
+                                    (list (lsp--command-get-title a) a))
+                                  actions)))
+        (cadr (assoc
+               (completing-read "Select code action: " name->action)
+               name->action)))
+    (error "No actions to select from")))
+
+(defun lsp-get-or-calculate-code-actions ()
+  "Get or calculate the current code actions.
+
+The method will either retrieve the current code actions or it will calculate the actual one."
+  (let ((current-code-action-params (lsp--text-document-code-action-params)))
+    (when (not (equal current-code-action-params lsp-code-action-params))
+      (let* ((request-params (lsp--make-request
+                             "textDocument/codeAction"
+                             (lsp--text-document-code-action-params)))
+             (actions (lsp--send-request request-params)))
+        (setq lsp-code-action-params current-code-action-params)
+        (lsp--set-code-action-params (current-buffer)
+                                     actions
+                                     current-code-action-params)))
+    lsp-code-actions))
+
+(defun lsp-execute-code-action (action)
+  "Execute code action ACTION.
+
+If ACTION is not set it will be selected from `lsp-code-actions'."
+  (interactive (list
+                (lsp--select-action (lsp-get-or-calculate-code-actions))))
+  (lsp--cur-workspace-check)
+  (let* ((command (gethash "command" action))
+         (action-handler (gethash command
+                                  (lsp--client-action-handlers
+                                   (lsp--workspace-client lsp--cur-workspace)))))
+    (if action-handler
+        (funcall action-handler action)
+      (lsp--execute-command action))))
+
+(defvar-local lsp-code-lenses nil
+  "A list of code lenses computed for the buffer.")
+
+(defun lsp--update-code-lenses (&optional callback)
+  "Update the list of code lenses for the current buffer.
+Optionally, CALLBACK is a function that accepts a single argument, the code lens object."
+  (lsp--cur-workspace-check)
+  (when callback
+    (cl-check-type callback function))
+  (when (gethash "codeLensProvider" (lsp--server-capabilities))
+    (lsp--send-request-async (lsp--make-request "textDocument/codeLens"
+                               `(:textDocument ,(lsp--text-document-identifier)))
+      (let ((buf (current-buffer)))
+        #'(lambda (lenses)
+            (with-current-buffer buf
+              (setq lsp-code-lenses lenses)
+              (when callback
+                (funcall callback lenses))))))))
+
+(defun lsp--make-document-formatting-options ()
+  (let ((json-false :json-false))
+    `(:tabSize ,tab-width :insertSpaces
+               ,(if indent-tabs-mode json-false t))))
+
+(defun lsp--make-document-formatting-params ()
+  `(:textDocument ,(lsp--text-document-identifier)
+                  :options ,(lsp--make-document-formatting-options)))
+
+(defun lsp-format-buffer ()
+  "Ask the server to format this document."
+  (interactive "*")
+  (unless (or (lsp--capability "documentFormattingProvider")
+              (lsp--registered-capability "textDocument/formatting"))
+    (signal 'lsp-capability-not-supported (list "documentFormattingProvider")))
+  (let ((edits (lsp--send-request (lsp--make-request
+                                   "textDocument/formatting"
+                                   (lsp--make-document-formatting-params)))))
+    (if (fboundp 'replace-buffer-contents)
+        (let ((current-buffer (current-buffer)))
+          (with-temp-buffer
+            (insert-buffer-substring-no-properties current-buffer)
+            (lsp--apply-text-edits edits)
+            (let ((temp-buffer (current-buffer)))
+              (with-current-buffer current-buffer
+                (replace-buffer-contents temp-buffer)))))
+      (let ((point (point))
+            (w-start (window-start)))
+        (lsp--apply-text-edits edits)
+        (goto-char point)
+        (goto-char (line-beginning-position))
+        (set-window-start (selected-window) w-start)))))
+
+(defun lsp--make-document-range-formatting-params (start end)
+  "Make DocumentRangeFormattingParams for selected region.
+interface DocumentRangeFormattingParams {
+    textDocument: TextDocumentIdentifier;
+    range: Range;
+    options: FormattingOptions;
+}"
+  (plist-put (lsp--make-document-formatting-params)
+             :range (lsp--region-to-range start end)))
+
+(defconst lsp--highlight-kind-face
+  '((1 . lsp-face-highlight-textual)
+    (2 . lsp-face-highlight-read)
+    (3 . lsp-face-highlight-write)))
+
+(defun lsp--remove-cur-overlays ()
+  (let ((overlays (lsp--workspace-highlight-overlays lsp--cur-workspace))
+         (buf (current-buffer)))
+    (dolist (overlay (gethash buf overlays))
+      (delete-overlay overlay))
+    (remhash buf overlays)))
+
+(defun lsp-symbol-highlight ()
+  "Highlight all relevant references to the symbol under point."
+  (interactive)
+  (lsp--send-request-async (lsp--make-request "textDocument/documentHighlight"
+                             (lsp--text-document-position-params))
+    (lsp--make-symbol-highlight-callback (current-buffer))))
+
+(defun lsp--make-symbol-highlight-callback (buf)
+  "Create a callback to process the reply of a
+'textDocument/documentHightlight' message for the buffer BUF.
+A reference is highlighted only if it is visible in a window."
+  (cl-check-type buf buffer)
+  (lambda (highlights)
+    (with-current-buffer buf
+      (lsp--remove-cur-overlays)
+      (when (and highlights (/= (length highlights) 0))
+        (let* ((windows-on-buffer (get-buffer-window-list nil nil 'visible))
+               (overlays (lsp--workspace-highlight-overlays lsp--cur-workspace))
+               (buf-overlays (gethash (current-buffer) overlays))
+               wins-visible-pos)
+          (save-restriction
+            (widen)
+            ;; Save visible portions of the buffer
+            (dolist (win windows-on-buffer)
+              (let* ((win-start (window-start win))
+                     (win-end (window-end win)))
+                (push (cons (1- (line-number-at-pos win-start))
+                            (1+ (line-number-at-pos win-end)))
+                      wins-visible-pos)))
+            (seq-doseq (highlight highlights)
+              (let* ((range (gethash "range" highlight nil))
+                     (kind (gethash "kind" highlight 1))
+                     (start (gethash "start" range))
+                     (end (gethash "end" range))
+                     overlay)
+                (dolist (win wins-visible-pos)
+                  (let* ((start-window (car win))
+                         (end-window (cdr win)))
+                    ;; Make the overlay only if the reference is visible
+                    (when (and (> (1+ (gethash "line" start)) start-window)
+                               (< (1+ (gethash "line" end)) end-window))
+                      (setq overlay (make-overlay (lsp--position-to-point start)
+                                                  (lsp--position-to-point end)))
+                      (overlay-put overlay 'face
+                                   (cdr (assq kind lsp--highlight-kind-face)))
+                      (push overlay buf-overlays)
+                      (puthash (current-buffer) buf-overlays overlays))))))))))))
+
+(defconst lsp--symbol-kind
+  '((1 . "File")
+     (2 . "Module")
+     (3 . "Namespace")
+     (4 . "Package")
+     (5 . "Class")
+     (6 . "Method")
+     (7 . "Property")
+     (8 . "Field")
+     (9 . "Constructor"),
+     (10 . "Enum")
+     (11 . "Interface")
+     (12 . "Function")
+     (13 . "Variable")
+     (14 . "Constant")
+     (15 . "String")
+     (16 . "Number")
+     (17 . "Boolean")
+     (18 . "Array")
+     (19 . "Object")
+     (20 . "Key")
+     (21 . "Null")
+     (22 . "Enum Member")
+     (23 . "Struct")
+     (24 . "Event")
+     (25 . "Operator")
+     (26 . "Type Parameter")))
+
+(defun lsp--symbol-information-to-xref (symbol)
+  "Return a `xref-item' from SYMBOL information."
+  (let* ((location (gethash "location" symbol))
+         (uri (gethash "uri" location))
+         (range (gethash "range" location))
+         (start (gethash "start" range)))
+    (xref-make (format "[%s] %s"
+                       (alist-get (gethash "kind" symbol) lsp--symbol-kind)
+                       (gethash "name" symbol))
+               (xref-make-file-location (lsp--uri-to-path uri)
+                                        (1+ (gethash "line" start))
+                                        (gethash "character" start)))))
+
+(defun lsp-format-region (s e)
+  (let ((edits (lsp--send-request (lsp--make-request
+                                   "textDocument/rangeFormatting"
+                                   (lsp--make-document-range-formatting-params s e)))))
+    (lsp--apply-text-edits edits)))
+
+(defun lsp--location-to-td-position (location)
+  "Convert LOCATION to a TextDocumentPositionParams object."
+  `(:textDocument (:uri ,(gethash "uri" location))
+                  :position ,(gethash "start" (gethash "range" location))))
+
+(defun lsp--symbol-info-to-identifier (symbol)
+  (let ((td-params (lsp--location-to-td-position (gethash "location" symbol))))
+    (propertize (gethash "name" symbol)
+      'ref-params (lsp--make-reference-params td-params)
+      'def-params td-params)))
+
+(defun lsp--get-document-symbols ()
+  (lsp--cur-workspace-check)
+  (lsp--send-request (lsp--make-request
+                       "textDocument/documentSymbol"
+                       `(:textDocument ,(lsp--text-document-identifier)))))
+
+(defun lsp--xref-backend () 'xref-lsp)
+
+(cl-defmethod xref-backend-identifier-at-point ((_backend (eql xref-lsp)))
+  (propertize (symbol-name (symbol-at-point))
+              'def-params (lsp--text-document-position-params)
+              'ref-params (lsp--make-reference-params)))
+
+(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
+  (let ((json-false :json-false)
+        (symbols (lsp--get-document-symbols)))
+    (seq-map #'lsp--symbol-info-to-identifier symbols)))
+
+;; (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql xref-lsp)))
+;;   nil)
+
+(cl-defmethod xref-backend-definitions ((_backend (eql xref-lsp)) identifier)
+  (let* ((maybeparams (get-text-property 0 'def-params identifier))
+         ;; In some modes (such as haskell-mode), xref-find-definitions gets
+         ;; called directly without applying the properties expected here. So we
+         ;; must test if the properties are present, and if not use the current
+         ;; point location.
+         (params (if (null maybeparams)
+                     (lsp--text-document-position-params)
+                   maybeparams))
+         (defs (lsp--send-request (lsp--make-request
+                                   "textDocument/definition"
+                                   params))))
+    (lsp--locations-to-xref-items (if (listp defs) defs (list defs)))))
+
+(cl-defmethod xref-backend-references ((_backend (eql xref-lsp)) identifier)
+  (let* ((properties (text-properties-at 0 identifier))
+         (params (plist-get properties 'ref-params))
+         (refs (lsp--send-request (lsp--make-request
+                                   "textDocument/references"
+                                   (or params (lsp--make-reference-params))))))
+    (lsp--locations-to-xref-items refs)))
+
+(cl-defmethod xref-backend-apropos ((_backend (eql xref-lsp)) pattern)
+  (let ((symbols (lsp--send-request (lsp--make-request
+                                     "workspace/symbol"
+                                     `(:query ,pattern)))))
+    (seq-map #'lsp--symbol-information-to-xref symbols)))
+
+(defun lsp--make-document-rename-params (newname)
+  "Make DocumentRangeFormattingParams for selected region.
+interface RenameParams {
+    textDocument: TextDocumentIdentifier;
+    position: Position;
+    newName: string;
+}"
+  `(:position ,(lsp--cur-position)
+              :textDocument ,(lsp--text-document-identifier)
+              :newName ,newname))
+
+(defun lsp-rename (newname)
+  "Rename the symbol (and all references to it) under point to NEWNAME."
+  (interactive (list (read-string "Rename to: " (thing-at-point 'symbol))))
+  (lsp--cur-workspace-check)
+  (unless (lsp--capability "renameProvider")
+    (signal 'lsp-capability-not-supported (list "renameProvider")))
+  (let ((edits (lsp--send-request (lsp--make-request
+                                   "textDocument/rename"
+                                   (lsp--make-document-rename-params newname)))))
+    (when edits
+      (lsp--apply-workspace-edit edits))))
+
+(defun lsp-find-custom (method &optional extra)
+  "Send request named METHOD and get cross references of the symbol under point.
+EXTRA is a plist of extra parameters."
+  (let ((loc (lsp--send-request
+              (lsp--make-request method
+                                 (append (lsp--text-document-position-params) extra)))))
+    (if loc
+        (xref--show-xrefs
+         (lsp--locations-to-xref-items (if (listp loc) loc (list loc))) nil)
+      (message "Not found for: %s" (thing-at-point 'symbol t)))))
+
+(defun lsp-goto-implementation ()
+  "Resolve, and go to the implementation(s) of the symbol under point."
+  (interactive)
+  (lsp--cur-workspace-check)
+  (unless (lsp--capability "implementationProvider")
+    (signal 'lsp-capability-not-supported (list "implementationProvider")))
+  (lsp-find-custom "textDocument/implementation"))
+
+(defun lsp-goto-type-definition ()
+  "Resolve, and go to the type definition(s) of the symbol under point."
+  (interactive)
+  (lsp--cur-workspace-check)
+  (unless (lsp--capability "typeDefinitionProvider")
+    (signal 'lsp-capability-not-supported (list "typeDefinitionProvider")))
+  (lsp-find-custom "textDocument/typeDefinition"))
+
+(define-inline lsp--execute-command (command)
+  "Given a COMMAND returned from the server, create and send a
+'workspace/executeCommand' message."
+  (inline-letevals (command)
+    (inline-quote
+      (progn
+        (cl-check-type ,command (satisfies lsp--command-p))
+        (lsp--send-execute-command
+          (gethash "command" ,command)
+          (gethash "arguments" ,command nil))))))
+
+(defun lsp--send-execute-command (command &optional args)
+  "Create and send a 'workspace/executeCommand' message having
+command COMMAND and optionsl ARGS"
+  (lsp--cur-workspace-check)
+  (unless (or (lsp--capability "executeCommandProvider")
+              (lsp--registered-capability "workspace/executeCommand"))
+    (signal 'lsp-capability-not-supported (list "executeCommandProvider")))
+  (lsp--send-request
+   (lsp--make-request
+    "workspace/executeCommand"
+    (lsp--make-execute-command-params command args))))
+
+(defun lsp--make-execute-command-params (cmd &optional args)
+  (if args
+      (list :command cmd :arguments args)
+    (list :command cmd)))
+
+(defalias 'lsp-point-to-position #'lsp--point-to-position)
+(defalias 'lsp-get-start-position #'lsp--get-start-position)
+(defalias 'lsp-get-end-position #'lsp--get-end-position)
+(defalias 'lsp-text-document-identifier #'lsp--text-document-identifier)
+(defalias 'lsp-send-execute-command #'lsp--send-execute-command)
+(defalias 'lsp-on-open #'lsp--text-document-did-open)
+(defalias 'lsp-on-save #'lsp--text-document-did-save)
+;; (defalias 'lsp-on-change #'lsp--text-document-did-change)
+(defalias 'lsp-completion-at-point #'lsp--get-completions)
+
+(defun lsp--unset-variables ()
+  (when lsp-enable-eldoc
+    (setq-local eldoc-documentation-function 'ignore))
+  (when lsp-enable-xref
+    (setq-local xref-backend-functions nil))
+  (when lsp-enable-completion-at-point
+    (remove-hook 'completion-at-point-functions #'lsp-completion-at-point t))
+  (remove-hook 'after-change-functions #'lsp-on-change t)
+  (remove-hook 'after-revert-hook #'lsp-on-revert t)
+  (remove-hook 'before-change-functions #'lsp-before-change t))
+
+(defun lsp--set-configuration (settings)
+  "Set the configuration for the lsp server."
+  (lsp--send-notification (lsp--make-notification
+                           "workspace/didChangeConfiguration"
+                           `(:settings , settings))))
+
+(defun lsp-workspace-register-watch (to-watch &optional workspace)
+  "Monitor for file change and trigger workspace/didChangeConfiguration.
+
+TO-WATCH is a list of the directories and regexp in the following format:
+'((root-dir1 (glob-pattern1 glob-pattern2))
+  (root-dir2 (glob-pattern3 glob-pattern4)))
+
+If WORKSPACE is not specified the `lsp--cur-workspace' will be used."
+  (setq workspace (or workspace lsp--cur-workspace))
+  (let ((watches (lsp--workspace-watches workspace)))
+    (cl-loop for (dir glob-patterns) in to-watch do
+      (lsp-create-watch
+        dir
+        (mapcar 'eshell-glob-regexp glob-patterns)
+        (lambda (event)
+          (let ((lsp--cur-workspace workspace))
+            (lsp-send-notification
+              (lsp-make-notification
+                "workspace/didChangeWatchedFiles"
+                (list :changes
+                  (list
+                    :type (alist-get (cadr event) lsp--file-change-type)
+                    :uri (lsp--path-to-uri (caddr event))))))))
+        watches))))
+
+(declare-function lsp-mode "lsp-mode" (&optional arg))
+
+(provide 'lsp-methods)
+;;; lsp-methods.el ends here