diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344')
16 files changed, 3460 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.el new file mode 100755 index 000000000000..0bf952fa7229 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.el @@ -0,0 +1,192 @@ +;; 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/>. + +;;; Code: + +(require 'compile) +(require 'url-util) +(require 'url-parse) +(require 'subr-x) +(require 'filenotify) +(require 'cl) + +(defconst lsp--message-type-face + `((1 . ,compilation-error-face) + (2 . ,compilation-warning-face) + (3 . ,compilation-message-face) + (4 . ,compilation-info-face))) + +(defcustom lsp-print-io nil + "If non-nil, print all messages to and from the language server to *Messages*." + :group 'lsp-mode + :type 'boolean) + +(defvar-local lsp--cur-workspace nil) + +(defvar lsp--uri-file-prefix (pcase system-type + (`windows-nt "file:///") + (_ "file://")) + "Prefix for a file-uri.") + +(defvar-local lsp-buffer-uri nil + "If set, return it instead of calculating it using `buffer-file-name'.") + +(define-error 'lsp-error "Unknown lsp-mode error") +(define-error 'lsp-empty-response-error + "Empty response from the language server" 'lsp-error) +(define-error 'lsp-timed-out-error + "Timed out while waiting for a response from the language server" 'lsp-error) +(define-error 'lsp-capability-not-supported + "Capability not supported by the language server" 'lsp-error) + +(defun lsp--propertize (str type) + "Propertize STR as per TYPE." + (propertize str 'face (alist-get type lsp--message-type-face))) + +(defvar lsp--no-response) + +;; from http://emacs.stackexchange.com/questions/8082/how-to-get-buffer-position-given-line-number-and-column-number +(defun lsp--position-to-point (params) + "Convert Position object in PARAMS to a point." + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line (gethash "line" params)) + (forward-char (gethash "character" params)) + (point)))) + +;;; TODO: Use the current LSP client name instead of lsp-mode for the type. +(defun lsp-warn (message &rest args) + "Display a warning message made from (`format-message' MESSAGE ARGS...). +This is equivalent to `display-warning', using `lsp-mode' as the type and +`:warning' as the level." + (display-warning 'lsp-mode (apply #'format-message message args))) + +(defvar lsp-message-project-root-warning nil + "Output the project root warning as a message and not to the *Warnings* buffer.") + +(defun lsp-make-traverser (name) + "Return a closure that walks up the current directory until NAME is found. +NAME can either be a string or a predicate used for `locate-dominating-file'. +The value returned by the function will be the directory name for NAME. + +If no such directory could be found, log a warning and return `default-directory'" + (lambda () + (let ((dir (locate-dominating-file "." name))) + (if dir + (file-truename dir) + (if lsp-message-project-root-warning + (message "Couldn't find project root, using the current directory as the root.") + (lsp-warn "Couldn't find project root, using the current directory as the root.") + default-directory))))) + +(defun lsp--get-uri-handler (scheme) + "Get uri handler for SCHEME in the current workspace." + (when lsp--cur-workspace + (gethash scheme (lsp--client-uri-handlers + (lsp--workspace-client lsp--cur-workspace))))) + +(defun lsp--uri-to-path (uri) + "Convert URI to a file path." + (let* ((url (url-generic-parse-url (url-unhex-string uri))) + (type (url-type url)) + (file (url-filename url))) + (if (and type (not (string= type "file"))) + (let ((handler (lsp--get-uri-handler type))) + (if handler + (funcall handler uri) + (error "Unsupported file scheme: %s" uri))) + ;; `url-generic-parse-url' is buggy on windows: + ;; https://github.com/emacs-lsp/lsp-mode/pull/265 + (or (and (eq system-type 'windows-nt) + (eq (elt file 0) ?\/) + (substring file 1)) + file)))) + +(define-inline lsp--buffer-uri () + "Return URI of the current buffer." + (inline-quote + (or lsp-buffer-uri (lsp--path-to-uri buffer-file-name)))) + +(define-inline lsp--path-to-uri (path) + "Convert PATH to a uri." + (inline-quote + (concat lsp--uri-file-prefix + (url-hexify-string (file-truename ,path) url-path-allowed-chars)))) + +(defun lsp--string-match-any (regex-list str) + "Given a list of REGEX-LIST and STR return the first matching regex if any." + (find-if (lambda (regex) (string-match regex str)) regex-list)) + +(defun lsp-create-watch (dir file-regexp-list callback &optional watches root-dir) + "Create recursive file notificaton watch in DIR monitoring FILE-REGEXP-LIST. +CALLBACK is the will be called when there are changes in any of +the monitored files. WATCHES is a hash table directory->file +notification handle which contains all of the watches that +already have been created. " + (let ((all-dirs (thread-last + (directory-files-recursively dir ".*" t) + (seq-filter (lambda (f) (file-directory-p f))) + (list* dir))) + (watches (or watches (make-hash-table :test 'equal))) + (root-dir (or root-dir dir))) + (seq-do + (lambda (dir-to-watch) + (puthash + dir-to-watch + (file-notify-add-watch + dir-to-watch + '(change) + (lambda (event) + (let ((file-name (caddr event)) + (event-type (cadr event))) + (cond + ((and (file-directory-p file-name) + (equal 'created event-type)) + + (lsp-create-watch file-name file-regexp-list callback watches root-dir) + + ;; process the files that are already present in + ;; the directory. + (thread-last + (directory-files-recursively file-name ".*" t) + (seq-do (lambda (f) + (when (and (lsp--string-match-any + file-regexp-list + (concat "/" (file-relative-name f root-dir))) + (not (file-directory-p f))) + (funcall callback (list nil 'created f))))))) + ((and (not (file-directory-p file-name)) + (lsp--string-match-any + file-regexp-list + (concat "/" (file-relative-name file-name root-dir)))) + (funcall callback event)))))) + watches)) + all-dirs) + watches)) + +(defun lsp-kill-watch (watches) + "Delete WATCHES." + (maphash + (lambda (_dir watch) + (file-notify-rm-watch watch)) + watches)) + +(declare-function lsp--workspace-client "lsp-methods" (cl-x)) +(declare-function lsp--client-uri-handlers "lsp-methods" (cl-x)) + +(provide 'lsp-common) +;;; lsp-common.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.elc new file mode 100644 index 000000000000..fdd6560daa7a --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-common.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.el new file mode 100644 index 000000000000..725098d05942 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.el @@ -0,0 +1,3 @@ +(user-error "Flycheck support has been moved to package lsp-ui. Please consult https://github.com/emacs-lsp/lsp-ui for documentation on lsp-ui-flycheck.") + +(provide 'lsp-flycheck) diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.elc new file mode 100644 index 000000000000..cd4bb7779375 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-flycheck.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.el new file mode 100644 index 000000000000..1c1869aaefa2 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.el @@ -0,0 +1,75 @@ +;; 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/>. + +;;; Commentary: + +;; Imenu integration with lsp-mode. Enable with: +;; (require 'lsp-imenu) +;; (add-hook 'lsp-after-open-hook 'lsp-enable-imenu) + +;;; Code: + +(require 'imenu) +(require 'lsp-methods) +(require 'seq) + +(defgroup lsp-imenu nil + "Customization group for `lsp-imenu'." + :group 'lsp-mode) + +(defcustom lsp-imenu-show-container-name t + "Display the symbol's container name in an imenu entry." + :type 'boolean + :group 'lsp-imenu) + +(defcustom lsp-imenu-container-name-separator "/" + "Separator string to use to separate the container name from the symbol while displaying imenu entries." + :type 'string + :group 'lsp-imenu) + +(define-inline lsp--point-to-marker (p) + (inline-quote (save-excursion (goto-char ,p) (point-marker)))) + +(defun lsp--symbol-to-imenu-elem (sym) + (let ((pt (lsp--position-to-point + (gethash "start" (gethash "range" (gethash "location" sym))))) + (name (gethash "name" sym)) + (container (gethash "containerName" sym))) + (cons (if (and lsp-imenu-show-container-name container) + (concat container lsp-imenu-container-name-separator name) + name) + (if imenu-use-markers (lsp--point-to-marker pt) pt)))) + +(defun lsp--symbol-filter (sym) + (not + (lsp--equal-files + (lsp--uri-to-path (gethash "uri" (gethash "location" sym))) + (buffer-file-name)))) + +(defun lsp--get-symbol-type (sym) + (or (cdr (assoc (gethash "kind" sym) lsp--symbol-kind)) "Other")) + +(defun lsp--imenu-create-index () + (let ((symbols (seq-remove #'lsp--symbol-filter (lsp--get-document-symbols)))) + (mapcar (lambda (nested-alist) + (cons (car nested-alist) + (mapcar #'lsp--symbol-to-imenu-elem (cdr nested-alist)))) + (seq-group-by #'lsp--get-symbol-type symbols)))) + +(defun lsp-enable-imenu () + (setq-local imenu-create-index-function #'lsp--imenu-create-index)) + +(provide 'lsp-imenu) +;;; lsp-imenu.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.elc new file mode 100644 index 000000000000..8ed3dc976c34 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-imenu.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.el new file mode 100644 index 000000000000..2204aa4faa2f --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.el @@ -0,0 +1,350 @@ +;; 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/>. + +;;; Code: + +(require 'json) +(require 'cl-lib) +(require 'lsp-common) +(require 'lsp-notifications) +(require 'pcase) +(require 'subr-x) + +;; vibhavp: Should we use a lower value (5)? +(defcustom lsp-response-timeout 10 + "Number of seconds to wait for a response from the language server before timing out." + :type 'number + :group 'lsp-mode) + +(defun lsp--send-wait (message proc parser) + "Send MESSAGE to PROC and wait for output from the process." + (when lsp-print-io + (let ((inhibit-message t)) + (message "lsp--stdio-wait: %s" message))) + (when (memq (process-status proc) '(stop exit closed failed nil)) + (error "%s: Cannot communicate with the process (%s)" (process-name proc) + (process-status proc))) + (process-send-string proc message) + (with-local-quit + (let* ((send-time (time-to-seconds (current-time))) + ;; max time by which we must get a response + (expected-time (+ send-time lsp-response-timeout))) + (while (lsp--parser-waiting-for-response parser) + ;; Wait for expected-time - current-time + (accept-process-output proc (- expected-time (time-to-seconds (current-time)))) + ;; We have timed out when expected-time < (current-time) + (when (< expected-time (time-to-seconds (current-time))) + (signal 'lsp-timed-out-error nil)))))) + +(defun lsp--send-no-wait (message proc) + "Send MESSAGE to PROC without waiting for further output." + (when lsp-print-io + (let ((inhibit-message t)) + (message "lsp--send-no-wait: %s" message))) + (when (memq (process-status proc) '(stop exit closed failed nil)) + (error "%s: Cannot communicate with the process (%s)" (process-name proc) + (process-status proc))) + (process-send-string proc message)) + +(cl-defstruct lsp--parser + (waiting-for-response nil) + (response-result nil) + (headers '()) ;; alist of headers + (body nil) ;; message body + (reading-body nil) ;; If non-nil, reading body + (body-length nil) ;; length of current message body + (body-received 0) ;; amount of current message body currently stored in 'body' + (leftovers nil) ;; Leftover data from previous chunk; to be processed + + (queued-notifications nil) ;; Unused field + (queued-requests nil) + + (workspace nil) ;; the workspace + ) + +(define-error 'lsp-parse-error + "Error parsing message from language server" 'lsp-error) +(define-error 'lsp-unknown-message-type + "Unknown message type" '(lsp-error lsp-parse-error)) +(define-error 'lsp-unknown-json-rpc-version + "Unknown JSON-RPC protocol version" '(lsp-error lsp-parse-error)) +(define-error 'lsp-no-content-length + "Content-Length header missing in message" '(lsp-error lsp-parse-error)) +(define-error 'lsp-invalid-header-name + "Invalid header name" '(lsp-error lsp-parse-error)) + +;; id method +;; x x request +;; x . response +;; . x notification + +(defun lsp--get-message-type (json-data) + "Get the message type from JSON-DATA." + (when (not (string= (gethash "jsonrpc" json-data "") "2.0")) + (signal 'lsp-unknown-json-rpc-version (list (gethash "jsonrpc" json-data)))) + (if (gethash "id" json-data nil) + (if (gethash "error" json-data nil) + 'response-error + (if (gethash "method" json-data nil) + 'request + 'response)) + (if (gethash "method" json-data nil) + 'notification + (signal 'lsp-unknown-message-type (list json-data))))) + +(defun lsp--default-message-handler (workspace params) + (lsp--window-show-message params workspace)) + +(defconst lsp--default-notification-handlers + #s(hash-table + test equal + data + ("window/showMessage" lsp--default-message-handler + "window/logMessage" lsp--default-message-handler + "textDocument/publishDiagnostics" (lambda (w p) (lsp--on-diagnostics p w)) + "textDocument/diagnosticsEnd" ignore + "textDocument/diagnosticsBegin" ignore))) + +(defun lsp--on-notification (p notification) + "Call the appropriate handler for NOTIFICATION." + (let* ((params (gethash "params" notification)) + (client (lsp--workspace-client (lsp--parser-workspace p))) + (method (gethash "method" notification)) + (handler (gethash method + (lsp--client-notification-handlers client) + (gethash method lsp--default-notification-handlers)))) + (if handler + (funcall handler (lsp--parser-workspace p) params) + (lsp-warn "Unknown method: %s" method)))) + +(defun lsp--on-request (p request) + "Call the appropriate handler for REQUEST, and send the return value to the server." + (let ((params (gethash "params" request)) + (client (lsp--workspace-client (lsp--parser-workspace p))) + (process (lsp--workspace-proc (lsp--parser-workspace p))) + (empty-response (lsp--make-response (gethash "id" request) nil nil)) + handler response) + (setq response + (pcase (gethash "method" request) + ("client/registerCapability" + (dolist (reg (gethash "registrations" params)) + (lsp--server-register-capability reg)) + empty-response) + ("window/showMessageRequest" + (let ((choice (lsp--window-show-message-request params))) + (lsp--make-response (gethash "id" request) + `(:title ,choice) + nil))) + ("client/unregisterCapability" + (dolist (unreg (gethash "unregisterations" params)) + (lsp--server-unregister-capability unreg)) + empty-response) + ("workspace/applyEdit" + (lsp--workspace-apply-edit-handler + (lsp--parser-workspace p) params) + empty-response) + (other + (setq handler (gethash other (lsp--client-request-handlers client) nil)) + (if (not handler) + (progn + (lsp-warn "Unknown request method: %s" other) + empty-response) + (lsp--make-response (gethash "id" request) + (funcall handler (lsp--parser-workspace p) params) nil))))) + ;; Send response to the server. + (lsp--send-no-wait (lsp--make-message response) process))) + +(defconst lsp--errors + '((-32700 "Parse Error") + (-32600 "Invalid Request") + (-32601 "Method not Found") + (-32602 "Invalid Parameters") + (-32603 "Internal Error") + (-32099 "Server Start Error") + (-32000 "Server End Error") + (-32002 "Server Not Initialized") + (-32001 "Unknown Error Code") + (-32800 "Request Cancelled")) + "alist of error codes to user friendly strings.") + +(defconst lsp--silent-errors '(-32800) + "Error codes that are okay to not notify the user about") + +(defun lsp--error-string (err) + "Format ERR as a user friendly string." + (let ((code (gethash "code" err)) + (message (gethash "message" err))) + (format "Error from the Language Server: %s (%s)" + message + (or (car (alist-get code lsp--errors)) "Unknown error")))) + +(defun lsp--get-body-length (headers) + (let ((content-length (cdr (assoc "Content-Length" headers)))) + (if content-length + (string-to-number content-length) + + ;; This usually means either the server our our parser is + ;; screwed up with a previous Content-Length + (error "No Content-Length header")))) + +(defun lsp--parse-header (s) + "Parse string S as a LSP (KEY . VAL) header." + (let ((pos (string-match "\:" s)) + key val) + (unless pos + (signal 'lsp-invalid-header-name (list s))) + (setq key (substring s 0 pos) + val (substring s (+ 2 pos))) + (when (string-equal key "Content-Length") + (cl-assert (cl-loop for c being the elements of val + when (or (> c ?9) (< c ?0)) return nil + finally return t) + nil (format "Invalid Content-Length value: %s" val))) + (cons key val))) + +(defun lsp--parser-reset (p) + (setf + (lsp--parser-leftovers p) "" + (lsp--parser-body-length p) nil + (lsp--parser-body-received p) nil + (lsp--parser-headers p) '() + (lsp--parser-body p) nil + (lsp--parser-reading-body p) nil)) + +(define-inline lsp--read-json (str) + (inline-quote + (let* ((json-array-type 'list) + (json-object-type 'hash-table) + (json-false nil)) + (json-read-from-string ,str)))) + +(defun lsp--parser-on-message (p msg) + "Called when the parser reads a complete message from the server." + (let* ((json-data (lsp--read-json msg)) + (id (gethash "id" json-data nil)) + (client (lsp--workspace-client (lsp--parser-workspace p))) + callback) + (pcase (lsp--get-message-type json-data) + ('response + (cl-assert id) + (setq callback (gethash (if (stringp id) + (string-to-number id) + id) + (lsp--client-response-handlers client) + nil)) + (if callback + (progn (funcall callback (gethash "result" json-data nil)) + (remhash id (lsp--client-response-handlers client))) + (setf (lsp--parser-response-result p) + (and json-data (gethash "result" json-data nil)) + (lsp--parser-waiting-for-response p) nil))) + ('response-error + (let* ((err (gethash "error" json-data nil)) + (code (gethash "code" err nil))) + (when (and json-data + (not (memq code lsp--silent-errors))) + (message (lsp--error-string err)))) + (setf (lsp--parser-response-result p) nil + (lsp--parser-waiting-for-response p) nil)) + ('notification (lsp--on-notification p json-data)) + ('request (lsp--on-request p json-data))))) + +(defun lsp--parser-read (p output) + (cl-assert (lsp--parser-workspace p) nil "Parser workspace cannot be nil.") + (let ((messages '()) + (chunk (concat (lsp--parser-leftovers p) output))) + (while (not (string-empty-p chunk)) + (if (not (lsp--parser-reading-body p)) + ;; Read headers + (let* ((body-sep-pos (string-match-p "\r\n\r\n" chunk))) + (if body-sep-pos + ;; We've got all the headers, handle them all at once: + (let* ((header-raw (substring chunk 0 body-sep-pos)) + (content (substring chunk (+ body-sep-pos 4))) + (headers + (mapcar 'lsp--parse-header + (split-string header-raw "\r\n"))) + (body-length (lsp--get-body-length headers))) + (setf + (lsp--parser-headers p) headers + (lsp--parser-reading-body p) t + (lsp--parser-body-length p) body-length + (lsp--parser-body-received p) 0 + (lsp--parser-body p) (make-string body-length ?\0) + (lsp--parser-leftovers p) nil) + (setq chunk content)) + + ;; Haven't found the end of the headers yet. Save everything + ;; for when the next chunk arrives and await further input. + (setf (lsp--parser-leftovers p) chunk) + (setq chunk ""))) + + ;; Read body + (let* ((total-body-length (lsp--parser-body-length p)) + (received-body-length (lsp--parser-body-received p)) + (chunk-length (string-bytes chunk)) + (left-to-receive (- total-body-length received-body-length)) + (this-body + (substring chunk 0 (min left-to-receive chunk-length))) + (leftovers (substring chunk (string-bytes this-body)))) + (store-substring (lsp--parser-body p) received-body-length this-body) + (setf (lsp--parser-body-received p) (+ (lsp--parser-body-received p) + (string-bytes this-body))) + (when (>= chunk-length left-to-receive) + ;; TODO: keep track of the Content-Type header, if + ;; present, and use its value instead of just defaulting + ;; to utf-8 + (push (decode-coding-string (lsp--parser-body p) 'utf-8) messages) + (lsp--parser-reset p)) + + (setq chunk leftovers)))) + (nreverse messages))) + +(defun lsp--json-pretty-print (msg) + "Convert json MSG string to pretty printed json string." + (let ((json-encoding-pretty-print t)) + (json-encode (json-read-from-string msg)))) + +(defun lsp--parser-make-filter (p ignore-regexps) + #'(lambda (_proc output) + (when (cl-loop for r in ignore-regexps + ;; check if the output is to be ignored or not + ;; TODO: Would this ever result in false positives? + when (string-match r output) return nil + finally return t) + (let ((messages + (condition-case err + (lsp--parser-read p output) + (error + (progn + (lsp--parser-reset p) + (setf (lsp--parser-response-result p) nil + (lsp--parser-waiting-for-response p) nil) + (error "Error parsing language server output: %s" err)))))) + + (dolist (m messages) + (when lsp-print-io + (let ((inhibit-message t)) + (message "Output from language server: %s" (lsp--json-pretty-print m)))) + (lsp--parser-on-message p m)))))) + +(declare-function lsp--client-notification-handlers "lsp-methods" (client)) +(declare-function lsp--client-request-handlers "lsp-methods" (client)) +(declare-function lsp--workspace-client "lsp-methods" (workspace)) +(declare-function lsp--workspace-apply-edit-handler "lsp-methods" (workspace params)) +(declare-function lsp--window-show-message-request "lsp-notifications" (params)) + +(provide 'lsp-io) +;;; lsp-io.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.elc new file mode 100644 index 000000000000..98db8fe27a70 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-io.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.el new file mode 100644 index 000000000000..4c4dd94429ce --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.el @@ -0,0 +1,2204 @@ +;; 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 + (mapc #'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. + + (mapc #'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 (mapcar #'lsp-diagnostic-original diags-in-range) 'vector))) + +(defconst lsp--completion-item-kind + `( + (1 . "Text") + (2 . "Method") + (3 . "Function") + (4 . "Constructor") + (5 . "Field") + (6 . "Variable") + (7 . "Class") + (8 . "Interface") + (9 . "Module") + (10 . "Property") + (11 . "Unit") + (12 . "Value") + (13 . "Enum") + (14 . "Keyword") + (15 . "Snippet") + (16 . "Color") + (17 . "File") + (18 . "Reference"))) + +(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 (alist-get (gethash "kind" table nil) lsp--completion-item-kind))) + (concat + " " + detail + (when kind " ") + (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)))) + (mapcar #'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 + (mapcar + (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 (listp contents) + (if render-all + contents + (list (car contents))) + (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))) + (dolist (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))) + (mapcar #'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))))) + (mapcar '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 (lsp--capability "executeCommandProvider") + (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 diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.elc new file mode 100644 index 000000000000..b77ff833a57e --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-methods.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-autoloads.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-autoloads.el new file mode 100644 index 000000000000..896390530bad --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-autoloads.el @@ -0,0 +1,108 @@ +;;; lsp-mode-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) + +;;;### (autoloads nil "lsp-methods" "lsp-methods.el" (23428 20482 +;;;;;; 431812 870000)) +;;; Generated autoloads from lsp-methods.el + +(let ((loads (get 'lsp-mode 'custom-loads))) (if (member '"lsp-methods" loads) nil (put 'lsp-mode 'custom-loads (cons '"lsp-methods" loads)))) + +(let ((loads (get 'lsp-faces 'custom-loads))) (if (member '"lsp-methods" loads) nil (put 'lsp-faces 'custom-loads (cons '"lsp-methods" loads)))) + +(defvar lsp-document-sync-method nil "\ +How to sync the document with the language server.") + +(custom-autoload 'lsp-document-sync-method "lsp-methods" t) + +(defvar 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.") + +(custom-autoload 'lsp-project-blacklist "lsp-methods" t) + +(defvar lsp-enable-eldoc t "\ +Enable `eldoc-mode' integration.") + +(custom-autoload 'lsp-enable-eldoc "lsp-methods" t) + +(defvar 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.") + +(custom-autoload 'lsp-eldoc-render-all "lsp-methods" t) + +(defvar lsp-highlight-symbol-at-point t "\ +Highlight the symbol under the point.") + +(custom-autoload 'lsp-highlight-symbol-at-point "lsp-methods" t) + +(defvar lsp-enable-codeaction t "\ +Enable code action processing.") + +(custom-autoload 'lsp-enable-codeaction "lsp-methods" t) + +(defvar lsp-enable-completion-at-point t "\ +Enable `completion-at-point' integration.") + +(custom-autoload 'lsp-enable-completion-at-point "lsp-methods" t) + +(defvar lsp-enable-xref t "\ +Enable xref integration.") + +(custom-autoload 'lsp-enable-xref "lsp-methods" t) + +(defvar lsp-enable-indentation t "\ +Indent regions using the file formatting functionality provided by the language server.") + +(custom-autoload 'lsp-enable-indentation "lsp-methods" t) + +(defvar lsp-before-save-edits t "\ +If non-nil, `lsp-mode' will apply edits suggested by the language server +before saving a document.") + +(custom-autoload 'lsp-before-save-edits "lsp-methods" t) + +(defvar lsp-hover-text-function 'lsp--text-document-hover-string "\ +The LSP method to use to display text on hover.") + +(custom-autoload 'lsp-hover-text-function "lsp-methods" t) + +(defface lsp-face-highlight-textual '((((background dark)) :background "saddle brown") (((background light)) :background "yellow")) "\ +Face used for textual occurances of symbols." :group (quote lsp-faces)) + +(defface lsp-face-highlight-read '((((background dark)) :background "firebrick") (((background light)) :background "red")) "\ +Face used for highlighting symbols being read." :group (quote lsp-faces)) + +(defface lsp-face-highlight-write '((((background dark)) :background "sea green") (((background light)) :background "green")) "\ +Face used for highlighting symbols being written to." :group (quote lsp-faces)) + +;;;*** + +;;;### (autoloads nil "lsp-mode" "lsp-mode.el" (23428 20482 436449 +;;;;;; 152000)) +;;; Generated autoloads from lsp-mode.el + +(autoload 'lsp-mode "lsp-mode" "\ + + +\(fn &optional ARG)" t nil) + +;;;*** + +;;;### (autoloads nil nil ("lsp-common.el" "lsp-flycheck.el" "lsp-imenu.el" +;;;;;; "lsp-io.el" "lsp-mode-pkg.el" "lsp-notifications.el") (23428 +;;;;;; 20482 439731 790000)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; lsp-mode-autoloads.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-pkg.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-pkg.el new file mode 100644 index 000000000000..232852a8c857 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode-pkg.el @@ -0,0 +1,10 @@ +(define-package "lsp-mode" "20180827.344" "Minor mode for interacting with Language Servers" + '((emacs "25.1")) + :authors + '(("Vibhav Pant" . "vibhavp@gmail.com")) + :maintainer + '("Vibhav Pant" . "vibhavp@gmail.com") + :url "https://github.com/emacs-lsp/lsp-mode") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.el new file mode 100644 index 000000000000..2a7e4b25b576 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.el @@ -0,0 +1,401 @@ +;;; lsp-mode.el --- Minor mode for interacting with Language Servers -*- lexical-binding: t -*- + +;; Copyright (C) 2016-2018 Vibhav Pant <vibhavp@gmail.com> + +;; 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/>. + +;; Author: Vibhav Pant <vibhavp@gmail.com> +;; URL: https://github.com/emacs-lsp/lsp-mode +;; Package-Requires: ((emacs "25.1")) +;; Version: 4.2 + +;;; Commentary: + +;;; Code: + +(require 'lsp-methods) +(require 'lsp-io) +(require 'cl-lib) +(require 'network-stream) + +(defvar lsp-version-support "3.0" + "This is the version of the Language Server Protocol currently supported by ‘lsp-mode’.") + +;;;###autoload +(define-minor-mode lsp-mode "" + nil nil nil + :lighter (:eval (lsp-mode-line)) + :group 'lsp-mode) + +(defun lsp--make-stdio-connection (name command command-fn stderr) + (lambda (filter sentinel) + (let* ((command (if command-fn (funcall command-fn) command)) + (final-command (if (consp command) command (list command)))) + (unless (executable-find (nth 0 final-command)) + (error (format "Couldn't find executable %s" (nth 0 final-command)))) + (let ((proc (make-process + :name name + :connection-type 'pipe + :coding 'no-conversion + :command final-command + :filter filter + :sentinel sentinel + :stderr stderr + :noquery t))) + ;; TODO: This is redundant with :noquery above, but due to a + ;; bug pre-Emacs 26 it is still needed + ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=30031 + (set-process-query-on-exit-flag (get-buffer-process (get-buffer stderr)) nil) + proc)))) + +(defun lsp--make-tcp-connection (name command command-fn host port stderr) + (lambda (filter sentinel) + (let* ((command (if command-fn (funcall command-fn) command)) + (final-command (if (consp command) command (list command))) + proc tcp-proc) + (unless (executable-find (nth 0 final-command)) + (error (format "Couldn't find executable %s" (nth 0 final-command)))) + (setq proc (make-process + :name name + :connection-type 'pipe + :coding 'no-conversion + :command final-command + :sentinel sentinel + :stderr stderr + :noquery t) + tcp-proc (open-network-stream (concat name " TCP connection") + nil host port + :type 'plain)) + ;; TODO: Same :noquery issue (see above) + (set-process-query-on-exit-flag (get-buffer-process (get-buffer stderr)) nil) + (set-process-query-on-exit-flag tcp-proc nil) + (set-process-filter tcp-proc filter) + (cons proc tcp-proc)))) + +(cl-defmacro lsp-define-whitelist-add (name get-root + &key docstring) + "Define a function to add the project root for the current buffer to the whitleist. +NAME is the base name for the command. +GET-ROOT is the language-specific function to determine the project root for the current buffer." + (let ((whitelist-add (intern (format "%s-whitelist-add" name))) + (enable-interactive (intern (format "%s-enable" name)))) + `(defun ,whitelist-add () + ,docstring + (interactive) + (let ((root (funcall ,get-root))) + (customize-save-variable 'lsp-project-whitelist + (add-to-list 'lsp-project-whitelist (lsp--as-regex root))) + (,enable-interactive))))) + +(cl-defmacro lsp-define-whitelist-remove (name get-root + &key docstring) + "Define a function to remove the project root for the current buffer from the whitleist. +NAME is the base name for the command. +GET-ROOT is the language-specific function to determine the project root for the current buffer." + (let ((whitelist-remove (intern (format "%s-whitelist-remove" name)))) + `(defun ,whitelist-remove () + ,docstring + (interactive) + (let ((root (funcall ,get-root))) + (customize-save-variable 'lsp-project-whitelist + (remove (lsp--as-regex root) lsp-project-whitelist)))))) + +(defun lsp--as-regex (root) + "Convert the directory path in ROOT to an equivalent regex." + (concat "^" (regexp-quote root) "$")) + +(cl-defmacro lsp-define-stdio-client (name language-id get-root command + &key docstring + language-id-fn + command-fn + ignore-regexps + ignore-messages + extra-init-params + initialize + prefix-function) + "Define a LSP client using stdio. +NAME is the symbol to use for the name of the client. +LANGUAGE-ID is the language id to be used when communication with +the Language Server. COMMAND is the command to run. + +Optional arguments: +`:docstring' is an optional docstring used for the entrypoint function created by +`lsp-define-stdio-client'. + +`:ignore-regexps' is a list of regexps. When a data packet from the LSP server + matches any of these regexps, it will be ignored. This is intended for dealing + with LSP servers that output non-protocol data. + +`:ignore-messages' is a list of regexps. When a message from the LSP 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 `logMessage's. + +`:command-fn' is a function that returns the command string/list to be used to + launch the language server. If non-nil, COMMAND is ignored. + +`:language-id-fn' is a function that returns the language-id string to be used + while opening a new file. If non-nil, LANGUAGE-ID is ignored. + +`:extra-init-params' is a plist that specifies any (optional) + initializeOptions parameters required by the LSP server. A function taking + a single argument (LSP workspace) and returning a plist is also accepted. + +`:initialize' is a function called when the client is initialized. It takes a + single argument, the newly created client. + +`:prefix-function' is a function 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." + (cl-check-type name symbol) + (let ((enable-name (intern (format "%s-enable" name)))) + `(progn + (lsp-define-whitelist-add ,name ,get-root) + (lsp-define-whitelist-remove ,name ,get-root) + (defun ,enable-name () + ,docstring + (interactive) + (lsp--enable-stdio-client ',name + :language-id ,language-id + :language-id-fn ,language-id-fn + :root-directory-fn ,get-root + :command ,command + :command-fn ,command-fn + :ignore-regexps ,ignore-regexps + :ignore-messages ,ignore-messages + :extra-init-params ,extra-init-params + :initialize-fn ,initialize + :enable-function (function ,enable-name) + :prefix-function ,prefix-function))))) + +(cl-defun lsp--enable-stdio-client (name &key language-id language-id-fn + root-directory-fn command command-fn + ignore-regexps ignore-messages + extra-init-params initialize-fn + enable-function + prefix-function) + (cl-check-type name symbol) + (cl-check-type language-id (or null string)) + (cl-check-type language-id-fn (or null function)) + (cl-check-type root-directory-fn (or null function)) + (cl-check-type command list) + (cl-check-type command-fn (or null function)) + (cl-check-type ignore-regexps list) + (cl-check-type ignore-messages list) + (cl-check-type extra-init-params (or list function)) + (cl-check-type initialize-fn (or null function)) + ;; (cl-check-type enable-function function) + (cl-check-type prefix-function (or null function)) + (when (and (not lsp-mode) (buffer-file-name)) + (let* ((stderr (generate-new-buffer-name + (concat "*" (symbol-name name) " stderr*"))) + (client (make-lsp--client + :language-id (or language-id-fn (lambda (_) language-id)) + :new-connection (lsp--make-stdio-connection + (symbol-name name) + command + command-fn + stderr) + :stderr stderr + :get-root root-directory-fn + :ignore-regexps ignore-regexps + :ignore-messages ignore-messages + :enable-function enable-function + :prefix-function prefix-function))) + (when initialize-fn + (funcall initialize-fn client)) + (let ((root (funcall (lsp--client-get-root client)))) + (if (lsp--should-start-p root) + (lsp--start client extra-init-params) + (message "Not initializing project %s" root)))))) + +(cl-defmacro lsp-define-tcp-client (name language-id get-root command host port + &key docstring + language-id-fn + command-fn + ignore-regexps + ignore-messages + extra-init-params + initialize + prefix-function) + "Define a LSP client using TCP. +NAME is the symbol to use for the name of the client. +LANGUAGE-ID is the language id to be used when communication with +the Language Server. COMMAND is the command to run. HOST is the +host address. PORT is the port number. + +Optional arguments: +`:ignore-regexps' is a list of regexps. When a data packet from the LSP server + matches any of these regexps, it will be ignored. This is intended for dealing + with LSP servers that output non-protocol data. + +`:ignore-messages' is a list of regexps. When a message from the LSP 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 `logMessage's. + +`:command-fn' is a function that returns the command string/list to be used to + launch the language server. If non-nil, COMMAND is ignored. + +`:language-id-fn' is a function that returns the language-id string to be used + while opening a new file. If non-nil, LANGUAGE-ID is ignored. + +`:extra-init-params' is a plist that specifies any (optional) + initializeOptions parameters required by the LSP server. A function taking + a single argument (LSP workspace) and returning a plist is also accepted. + +`:initialize' is a function called when the client is initialized. It takes a + single argument, the newly created client. + +`:prefix-function' is a function 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." + (cl-check-type name symbol) + (let ((enable-name (intern (format "%s-enable" name)))) + `(progn + (lsp-define-whitelist-add ,name ,get-root) + (lsp-define-whitelist-remove ,name ,get-root) + (defun ,enable-name () + ,docstring + (interactive) + (lsp--enable-tcp-client ',name + :language-id ,language-id + :language-id-fn ,language-id-fn + :root-directory-fn ,get-root + :command ,command + :command-fn ,command-fn + :host ,host + :port ,port + :ignore-regexps ,ignore-regexps + :ignore-messages ,ignore-messages + :extra-init-params ,extra-init-params + :initialize-fn ,initialize + :enable-function (function ,enable-name) + :prefix-function ,prefix-function))))) + +(cl-defun lsp--enable-tcp-client (name &key language-id language-id-fn + root-directory-fn command command-fn + host port + ignore-regexps ignore-messages + extra-init-params initialize-fn + enable-function + prefix-function) + (cl-check-type name symbol) + (cl-check-type language-id (or null string)) + (cl-check-type language-id-fn (or null function)) + (cl-check-type root-directory-fn (or null function)) + (cl-check-type command list) + (cl-check-type command-fn (or null function)) + (cl-check-type host string) + (cl-check-type port (integer 1 #xFFFF)) + (cl-check-type ignore-regexps list) + (cl-check-type ignore-messages list) + (cl-check-type extra-init-params (or list function)) + (cl-check-type initialize-fn (or null function)) + (cl-check-type prefix-function (or null function)) + (when (and (not lsp-mode) (buffer-file-name)) + (let* ((stderr (generate-new-buffer-name + (concat "*" (symbol-name name) " stderr*"))) + (client (make-lsp--client + :language-id (or language-id-fn (lambda (_) language-id)) + :new-connection (lsp--make-tcp-connection + (symbol-name name) + command + command-fn + host port + stderr) + :stderr stderr + :get-root root-directory-fn + :ignore-regexps ignore-regexps + :ignore-messages ignore-messages + :enable-function enable-function + :prefix-function prefix-function))) + (when initialize-fn + (funcall initialize-fn client)) + (let ((root (funcall (lsp--client-get-root client)))) + (if (lsp--should-start-p root) + (lsp--start client extra-init-params) + (message "Not initializing project %s" root)))))) + +(defvar-local lsp-status nil + "The current status of the LSP server.") + +(defun lsp-workspace-status (status-string &optional workspace) + "Set current workspace status to STATUS-STRING. +If WORKSPACE is not specified defaults to lsp--cur-workspace." + (setf (lsp--workspace-status (or workspace lsp--cur-workspace)) status-string)) + +(defun lsp-mode-line () + "Construct the mode line text." + (concat " LSP" lsp-status (lsp--workspace-status lsp--cur-workspace))) + +(defconst lsp--sync-type + `((0 . "None") + (1 . "Full Document") + (2 . "Incremental Changes"))) + +(defconst lsp--capabilities + `(("textDocumentSync" . ("Document sync method" . + ((0 . "None") + (1 . "Send full contents") + (2 . "Send incremental changes.")))) + ("hoverProvider" . ("The server provides hover support" . boolean)) + ("completionProvider" . ("The server provides completion support" . boolean)) + ("signatureHelpProvider" . ("The server provides signature help support" . boolean)) + ("definitionProvider" . ("The server provides goto definition support" . boolean)) + ("typeDefinitionProvider" . ("The server provides goto type definition support" . boolean)) + ("implementationProvider" . ("The server provides goto implementation support" . boolean)) + ("referencesProvider" . ("The server provides references support" . boolean)) + (("documentHighlightProvider" . ("The server provides document highlight support." . boolean))) + ("documentSymbolProvider" . ("The server provides file symbol support" . boolean)) + ("workspaceSymbolProvider" . ("The server provides project symbol support" . boolean)) + ("codeActionProvider" . ("The server provides code actions" . boolean)) + ("codeLensProvider" . ("The server provides code lens" . boolean)) + ("documentFormattingProvider" . ("The server provides file formatting" . boolean)) + ("documentOnTypeFormattingProvider" . ("The server provides on-type formatting" . boolean)) + ("documentLinkProvider" . ("The server provides document link support" . boolean)) + ("executeCommandProvider" . ("The server provides command execution support" . boolean)) + (("documentRangeFormattingProvider" . ("The server provides region formatting" . boolean))) + (("renameProvider" . ("The server provides rename support" . boolean))))) + +(defun lsp--cap-str (cap) + (let* ((elem (assoc cap lsp--capabilities)) + (desc (cadr elem)) + (type (cddr elem)) + (value (gethash cap (lsp--server-capabilities)))) + (when (and elem desc type value) + (concat desc (cond + ((listp type) (concat ": " (cdr (assoc value type))))) "\n")))) + +(defun lsp-capabilities () + "View all capabilities for the language server associated with this buffer." + (interactive) + (unless lsp--cur-workspace + (user-error "No language server is associated with this buffer")) + (let ((str (mapconcat #'lsp--cap-str (reverse (hash-table-keys + (lsp--server-capabilities))) "")) + (buffer-name (generate-new-buffer-name "lsp-capabilities")) + ) + (get-buffer-create buffer-name) + (with-current-buffer buffer-name + (view-mode -1) + (erase-buffer) + (insert str) + (view-mode 1)) + (switch-to-buffer buffer-name))) + +(provide 'lsp-mode) +;;; lsp-mode.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.elc new file mode 100644 index 000000000000..00e15cc55a0b --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-mode.elc Binary files differdiff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.el b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.el new file mode 100644 index 000000000000..82935a5ac9e3 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.el @@ -0,0 +1,117 @@ +;; 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/>. + +;;; Code: + +(require 'lsp-common) +(require 'cl-lib) +(require 'subr-x) + +(defcustom lsp-inhibit-message nil + "If non-nil, inhibit the message echo via `inhibit-message'." + :type 'boolean + :group 'lsp-mode) + +(defun lsp--window-show-message (params &optional workspace) + "Send the server's messages to message, inhibit if `lsp-inhibit-message' is set, +or the message matches one of this client's :ignore-messages" + (let* ((inhibit-message (or inhibit-message lsp-inhibit-message)) + (message (gethash "message" params)) + (client (lsp--workspace-client workspace))) + (when (or (not client) + (cl-notany (lambda (r) (string-match-p r message)) + (lsp--client-ignore-messages client))) + (message "%s" (lsp--propertize message (gethash "type" params)))))) + +(defun lsp--window-show-message-request (params) + "Display a message request to the user and send the user's +selection back to the server." + (let* ((type (gethash "type" params)) + (message (lsp--propertize (gethash "message" params) type)) + (choices (mapcar (lambda (choice) (gethash "title" choice)) + (gethash "actions" params)))) + (if choices + (completing-read (concat message " ") choices nil t) + (message message)))) + +(defcustom lsp-after-diagnostics-hook nil + "Hooks to run after diagnostics are received from the language +server and put in `lsp--diagnostics'." + :type 'hook + :group 'lsp-mode) + +(defvar lsp--diagnostics (make-hash-table :test 'equal) + "Hash table storing the diagnostics per file.") + +(cl-defstruct lsp-diagnostic + (range nil :read-only t) + ;; range has the form (:start (:line N :column N) :end (:line N :column N) ) + ;; where N are zero-indexed numbers + (line nil :read-only t) + (column nil :read-only t) + (severity nil :read-only t) ;; 1 - error, 2 - warning, 3 - information, 4 - hint + (code nil :read-only t) ;; the diagnostic's code + (source nil :read-only t) ;; + (message nil :read-only t) ;; diagnostic's message + (original nil :read-only t) ;; original diagnostic from LSP, kept for when it + ;; needs to be sent back in e.g. codeAction + ;; context. + ) + +(defun lsp--make-diag (diag) + "Make a `lsp-diagnostic' from DIAG." + (let* ((range (gethash "range" diag)) + (start (gethash "start" range)) + (end (gethash "end" range)) + (message (gethash "message" diag)) + (source (gethash "source" diag))) + (make-lsp-diagnostic + :range (list :start (list :line (gethash "line" start) + :column (gethash "character" start)) + :end (list :line (gethash "line" end) + :column (gethash "character" end))) + :line (gethash "line" start) + :column (gethash "character" start) + :severity (gethash "severity" diag) + :code (gethash "code" diag) + :source (gethash "source" diag) + :message (if source (format "%s: %s" source message) message) + :original diag))) + +(defun lsp--equal-files (f1 f2) + (and f1 f2 + (string-equal (file-truename f1) (file-truename f2)))) + +(defun lsp--on-diagnostics (params workspace) + "Callback for textDocument/publishDiagnostics. +interface PublishDiagnosticsParams { + uri: string; + diagnostics: Diagnostic[]; +}" + (let ((file (lsp--uri-to-path (gethash "uri" params))) + (diagnostics (gethash "diagnostics" params)) buffer) + (setq buffer (cl-loop for buffer in (lsp--workspace-buffers workspace) + when (lsp--equal-files (buffer-file-name buffer) file) + return buffer + finally return nil)) + (when buffer + (puthash file (mapcar #'lsp--make-diag diagnostics) lsp--diagnostics) + (with-current-buffer buffer + (run-hooks 'lsp-after-diagnostics-hook))))) + +(declare-function lsp--workspace-buffers "lsp-methods" (workspace)) + +(provide 'lsp-notifications) +;;; lsp-notifications.el ends here diff --git a/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.elc b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.elc new file mode 100644 index 000000000000..e10013ca0228 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/lsp-mode-20180827.344/lsp-notifications.elc Binary files differ |