From 17ee0e400bef47c371afcae76037f9ea6a44ad13 Mon Sep 17 00:00:00 2001 From: William Carroll Date: Mon, 10 Sep 2018 14:51:14 -0400 Subject: Support Vim, Tmux, Emacs with Stow After moving off of Meta, Dotfiles has a greater responsibility to manage configs. Vim, Tmux, and Emacs are now within Stow's purview. --- .../elpa/company-lsp-20180828.438/company-lsp.el | 435 +++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 configs/shared/emacs/.emacs.d/elpa/company-lsp-20180828.438/company-lsp.el (limited to 'configs/shared/emacs/.emacs.d/elpa/company-lsp-20180828.438/company-lsp.el') diff --git a/configs/shared/emacs/.emacs.d/elpa/company-lsp-20180828.438/company-lsp.el b/configs/shared/emacs/.emacs.d/elpa/company-lsp-20180828.438/company-lsp.el new file mode 100644 index 000000000000..884aff9e20f0 --- /dev/null +++ b/configs/shared/emacs/.emacs.d/elpa/company-lsp-20180828.438/company-lsp.el @@ -0,0 +1,435 @@ +;;; company-lsp.el --- Company completion backend for lsp-mode. -*- lexical-binding: t -*- + +;; Version: 2.0.2 +;; Package-Version: 20180828.438 +;; Package-Requires: ((emacs "25.1") (lsp-mode "3.4") (company "0.9.0") (s "1.2.0") (dash "2.11.0")) +;; URL: https://github.com/tigersoldier/company-lsp + +;; 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 . + +;;; Commentary: + +;; `company-lsp' is a `company' completion backend for `lsp-mode'. +;; To use it, add `company-lsp' to `company-backends': + +;; (require 'company-lsp) +;; (push 'company-lsp company-backends) + +;;; Code: + +(require 'cl-lib) +(require 'company) +(require 'lsp-mode) +(require 's) +(require 'dash) + +(defgroup company-lsp nil + "Company completion backend for lsp-mode." + :prefix "company-lsp-" + :group 'tools) + +(defcustom company-lsp-cache-candidates 'auto + "Whether or not to cache completion candidates. + +When set to 'auto, company-lsp caches the completion. It sends +incremental completion requests to the server if and only if the +cached results are incomplete. The candidate list may not be +sorted or filtered as the server would for cached completion +results. + +When set to t, company-mode caches the completion. It won't send +incremental completion requests to the server. + +When set to nil, results are not cached at all. The candidates +are always sorted and filtered by the server. Use this option if +the server handles caching for incremental completion or +sorting/matching provided by the server is critical." + :type '(choice (const :tag "Respect server response" auto) + (const :tag "Always cache" t) + (const :tag "Never cache" nil)) + :group 'company-lsp) + +(defcustom company-lsp-async t + "Whether or not to use async operations to fetch data." + :type 'boolean + :group 'company-lsp) + +(defcustom company-lsp-enable-snippet t + "Whether or not to support expanding completion snippet. + +If set to non-nil, company-lsp will register client capabilities +for snippet support. When the server returns completion item with +snippet, company-lsp will replace the label of the completion +item with the snippet and use yas-snippet to expand it." + :type 'boolean + :group 'company-lsp) + +(defcustom company-lsp-enable-recompletion nil + "Whether or not to re-trigger completion for trigger characters. + +If set to non-nil, when company-lsp finishes completion, it checks if +the current point is before any completion trigger characters. If yes, +it re-triggers another completion request. + +This is useful in cases such as 'std' is completed as 'std::' in C++." + :type 'boolean + :group 'company-lsp) + +(declare-function yas-expand-snippet "ext:yasnippet.el") + +(defvar company-lsp--snippet-functions '(("rust" . company-lsp--rust-completion-snippet)) + "Alist of functions to insert our snippets for each language.") + +(defvar-local company-lsp--completion-cache nil + "Cached completion. It's an alist of (prefix . completion). + +PREFIX is the prefix string. +COMPLETION is a cache-item created by `company-lsp--cache-item-new'.") + +(defun company-lsp--trigger-characters () + "Return a list of completion trigger characters specified by server." + (let ((provider (lsp--capability "completionProvider"))) + (and provider (gethash "triggerCharacters" provider)))) + +(defun company-lsp--completion-prefix () + "Return the completion prefix. + +Return value is compatible with the `prefix' command of a company backend. + +Return nil if no completion should be triggered. Return a string +as the prefix to be completed, or a cons cell of (prefix . t) to bypass +`company-minimum-prefix-length' for trigger characters." + (let ((trigger-chars (company-lsp--trigger-characters))) + (if trigger-chars + (let* ((max-trigger-len (apply 'max (mapcar (lambda (trigger-char) + (length trigger-char)) + trigger-chars))) + (trigger-regex (s-join "\\|" (mapcar #'regexp-quote trigger-chars))) + (symbol-cons (company-grab-symbol-cons trigger-regex max-trigger-len))) + ;; Some major modes define trigger characters as part of the symbol. For + ;; example "@" is considered a vaild part of symbol in java-mode. + ;; Company will grab the trigger character as part of the prefix while + ;; the server doesn't. Remove the leading trigger character to solve + ;; this issue. + (let* ((symbol (if (consp symbol-cons) + (car symbol-cons) + symbol-cons)) + (trigger-char (seq-find (lambda (trigger-char) + (s-starts-with? trigger-char symbol)) + trigger-chars))) + (if trigger-char + (cons (substring symbol (length trigger-char)) t) + symbol-cons))) + (company-grab-symbol)))) + +(defun company-lsp--make-candidate (item prefix) + "Convert a CompletionItem JSON data to a string. + +ITEM is a hashtable representing the CompletionItem interface. +PREFIX is the currently active prefix. + +The returned string has a lsp-completion-item property with the +value of ITEM." + ;; The property has to be the same as added by `lsp--make-completion-item' so + ;; that `lsp--annotate' can use it. + (propertize (gethash "label" item) 'lsp-completion-item item 'lsp-completion-prefix prefix)) + +(defun company-lsp--candidate-item (candidate) + "Retrieve the CompletionItem hashtable associated with CANDIDATE. + +CANDIDATE is a string returned by `company-lsp--make-candidate'." + (plist-get (text-properties-at 0 candidate) 'lsp-completion-item)) + +(defun company-lsp--candidate-prefix (candidate) + "Retrieves the prefix that was active during creation of the candidate. + +CANDIDATE is a string returned by `company-lsp--make-candidate'." + (plist-get (text-properties-at 0 candidate) 'lsp-completion-prefix)) + +(defun company-lsp--resolve-candidate (candidate &rest props) + "Resolve a completion candidate to fill some properties. + +CANDIDATE is a string returned by `company-lsp--make-candidate'. +PROPS are strings of property names of CompletionItem hashtable +to be resolved. + +The completionItem/resolve request will only be sent to the +server if the candidate has not been resolved before, and at lest +one of the PROPS of the CompletionItem is missing. + +Returns CANDIDATE with the resolved CompletionItem." + (unless (plist-get (text-properties-at 0 candidate) 'company-lsp-resolved) + (let ((item (company-lsp--candidate-item candidate))) + (when (seq-some (lambda (prop) + (null (gethash prop item))) + props) + (let ((resolved-item (lsp--resolve-completion item)) + (len (length candidate))) + (put-text-property 0 len + 'lsp-completion-item resolved-item + candidate) + (put-text-property 0 len + 'company-lsp-resolved t + candidate))))) + candidate) + +(defun company-lsp--rust-completion-snippet (item) + "Function providing snippet with the rust language. +It parses the function's signature in ITEM (a CompletionItem) +to expand its arguments." + (-when-let* ((kind (gethash "kind" item)) + (is-function (= kind 3))) + (let* ((detail (gethash "detail" item)) + (snippet (when (and detail (s-matches? "^\\(pub \\)?\\(unsafe \\)?fn " detail)) + (-some--> (substring detail (1+ (s-index-of "(" detail)) (s-index-of ")" detail)) + (replace-regexp-in-string "^[^,]*self\\(, \\)?" "" it) + (and (not (s-blank-str? it)) it) + (s-split ", " it) + (mapconcat (lambda (x) (format "${%s}" x)) it ", "))))) + (concat "(" (or snippet "$1") ")$0")))) + +(defun company-lsp--fallback-snippet (item) + "Return the fallback snippet to expand for ITEM. + +It looks for function corresponding to the language in +`company-lsp--snippet-functions'. + +ITEM is a hashtable of the CompletionItem message. + +Return a string of the snippet to expand, or nil if no snippet is available." + (-when-let* ((language-id-fn (lsp--client-language-id (lsp--workspace-client lsp--cur-workspace))) + (language-id (funcall language-id-fn (current-buffer))) + (fn-cons (assoc language-id company-lsp--snippet-functions)) + (fn (cdr fn-cons))) + (funcall fn item))) + +(defun company-lsp--looking-back-trigger-characters-p () + "Return non-nil if text before point matches any of the trigger characters." + (let ((trigger-chars (company-lsp--trigger-characters))) + (cl-some (lambda (trigger-char) + (equal (buffer-substring-no-properties (- (point) (length trigger-char)) (point)) + trigger-char)) + trigger-chars))) + +(defun company-lsp--post-completion (candidate) + "Replace a CompletionItem's label with its insertText. Apply text edits. + +CANDIDATE is a string returned by `company-lsp--make-candidate'." + (let* ((resolved-candidate (company-lsp--resolve-candidate candidate + "insertText" + "textEdit" + "additionalTextEdits")) + (item (company-lsp--candidate-item resolved-candidate)) + (prefix (company-lsp--candidate-prefix candidate)) + (label (gethash "label" item)) + (start (- (point) (length label))) + (insert-text (gethash "insertText" item)) + ;; 1 = plaintext, 2 = snippet + (insert-text-format (gethash "insertTextFormat" item)) + (text-edit (gethash "textEdit" item)) + (additional-text-edits (gethash "additionalTextEdits" item))) + (cond + (text-edit + (setq insert-text (gethash "newText" text-edit)) + (delete-region (- (point) (length candidate)) (point)) + (insert prefix) + (let* ((range (gethash "range" text-edit)) + (start-point (lsp--position-to-point (gethash "start" range))) + (new-text-length (length insert-text))) + (lsp--apply-text-edit text-edit) + (goto-char (+ start-point new-text-length)))) + ((and insert-text (not (eq insert-text-format 2))) + (cl-assert (string-equal (buffer-substring-no-properties start (point)) label)) + (goto-char start) + (delete-char (length label)) + (insert insert-text))) + + (let ((start-marker (set-marker (make-marker) start))) + (when additional-text-edits + (lsp--apply-text-edits additional-text-edits)) + (when (and company-lsp-enable-snippet + (fboundp 'yas-expand-snippet)) + (if (and insert-text (eq insert-text-format 2)) + (yas-expand-snippet insert-text (marker-position start-marker) (point)) + (-when-let (fallback-snippet (company-lsp--fallback-snippet item)) + (yas-expand-snippet fallback-snippet)))) + (set-marker start-marker nil)) + ;; Here we set this-command to a `self-insert-command' + ;; so that company may retrigger idle completion after the snippet expansion + ;; (~`company-post-command'). + ;; This is a bit of a hack and maybe that will change in the future. + ;; This is useful for example when the completed candidate is a namespace + ;; and the annotation text (inserted snippet) is the scope operator. + ;; + ;; std| -> std:: (=> idle completion desired here) + ;; stderr + ;; ... + ;; + ;; See https://github.com/company-mode/company-mode/issues/143 + (when (and company-lsp-enable-recompletion + (company-lsp--looking-back-trigger-characters-p)) + (setq this-command 'self-insert-command)))) + +(defun company-lsp--on-completion (response prefix) + "Handle completion RESPONSE. + +PREFIX is a string of the prefix when the completion is requested. + +Return a list of strings as the completion candidates." + (let* ((incomplete (and (hash-table-p response) (gethash "isIncomplete" response))) + (items (cond ((hash-table-p response) (gethash "items" response)) + ((sequencep response) response))) + (candidates (mapcar (lambda (item) + (company-lsp--make-candidate item prefix)) + (lsp--sort-completions items)))) + (when (null company-lsp--completion-cache) + (add-hook 'company-completion-cancelled-hook #'company-lsp--cleanup-cache) + (add-hook 'company-completion-finished-hook #'company-lsp--cleanup-cache)) + (when (eq company-lsp-cache-candidates 'auto) + ;; Only cache candidates on auto mode. If it's t company caches the + ;; candidates for us. + (company-lsp--cache-put prefix (company-lsp--cache-item-new candidates incomplete))) + candidates)) + +(defun company-lsp--cleanup-cache (_) + "Clean up completion cache and company hooks." + (setq company-lsp--completion-cache nil) + (remove-hook 'company-completion-finished-hook #'company-lsp--cleanup-cache) + (remove-hook 'company-completion-cancelled-hook #'company-lsp--cleanup-cache)) + +(defun company-lsp--cache-put (prefix candidates) + "Set cache for PREFIX to be CANDIDATES. + +CANDIDATES is a cache item created by `company-lsp--cache-item-new'." + (setq company-lsp--completion-cache + (cons (cons prefix candidates) + company-lsp--completion-cache))) + +(defun company-lsp--cache-get (prefix) + "Get the cached completion for PREFIX. + +Return a cache item if cache for PREFIX exists. Otherwise return nil." + (let ((cache (cdr (assoc prefix company-lsp--completion-cache))) + (len (length prefix)) + previous-cache) + (if cache + cache + (cl-dotimes (i len) + (when (setq previous-cache + (cdr (assoc (substring prefix 0 (- len i 1)) + company-lsp--completion-cache))) + (if (company-lsp--cache-item-incomplete-p previous-cache) + (cl-return nil) + ;; TODO: Allow customizing matching functions to support fuzzy matching. + ;; Consider supporting company-flx out of box. + (let* ((previous-candidates (company-lsp--cache-item-candidates previous-cache)) + (new-candidates (all-completions prefix previous-candidates)) + (new-cache (company-lsp--cache-item-new new-candidates nil))) + (company-lsp--cache-put prefix new-cache) + (cl-return new-cache)))))))) + +(defun company-lsp--cache-item-new (candidates incomplete) + "Create a new cache item. + +CANDIDATES: A list of strings. The completion candidates. +INCOMPLETE: t or nil. Whether the candidates are incomplete or not." + (list :incomplete incomplete :candidates candidates)) + +(defun company-lsp--cache-item-incomplete-p (cache-item) + "Determine whether a CACHE-ITEM is incomplete." + (plist-get cache-item :incomplete)) + +(defun company-lsp--cache-item-candidates (cache-item) + "Get candidates from a CACHE-ITEM." + (plist-get cache-item :candidates)) + +(defun company-lsp--documentation (candidate) + "Get the documentation from the item in the CANDIDATE. + +The documentation can be either string or MarkupContent. This method +will return markdown string if it is MarkupContent, original string +otherwise. If the documentation is not present, it will return nil +which company can handle." + (let* ((resolved-candidate (company-lsp--resolve-candidate candidate "documentation")) + (item (company-lsp--candidate-item resolved-candidate)) + (documentation (gethash "documentation" item))) + (if + (hash-table-p documentation) ;; If true, then the documentation is a MarkupContent. String otherwise. + (gethash "value" documentation) + documentation))) + +(defun company-lsp--candidates-sync (prefix) + "Get completion candidates synchronously. + +PREFIX is the prefix string for completion. + +Return a list of strings as completion candidates." + (let ((req (lsp--make-request "textDocument/completion" + (lsp--text-document-position-params)))) + (company-lsp--on-completion (lsp--send-request req) prefix))) + +(defun company-lsp--candidates-async (prefix callback) + "Get completion candidates asynchronously. + +PREFIX is the prefix string for completion. +CALLBACK is a function that takes a list of strings as completion candidates." + (let ((req (lsp--make-request "textDocument/completion" + (lsp--text-document-position-params)))) + (lsp--send-request-async req + (lambda (resp) + (funcall callback (company-lsp--on-completion resp prefix)))))) + +;;;###autoload +(defun company-lsp (command &optional arg &rest _) + "Define a company backend for lsp-mode. + +See the documentation of `company-backends' for COMMAND and ARG." + (interactive (list 'interactive)) + (cl-case command + (interactive (company-begin-backend #'company-lsp)) + (prefix (and + (bound-and-true-p lsp-mode) + (lsp--capability "completionProvider") + (not (company-in-string-or-comment)) + (or (company-lsp--completion-prefix) 'stop))) + (candidates + ;; If the completion items in the response have textEdit action populated, + ;; we'll apply them in `company-lsp--post-completion'. However, textEdit + ;; actions only apply to the pre-completion content. We backup the current + ;; prefix and restore it after company completion is done, so the content + ;; is restored and textEdit actions can be applied. + (or (company-lsp--cache-item-candidates (company-lsp--cache-get arg)) + (and company-lsp-async + (cons :async (lambda (callback) + (company-lsp--candidates-async arg callback)))) + (company-lsp--candidates-sync arg))) + (sorted t) + (no-cache (not (eq company-lsp-cache-candidates t))) + (annotation (lsp--annotate arg)) + (quickhelp-string (company-lsp--documentation arg)) + (doc-buffer (company-doc-buffer (company-lsp--documentation arg))) + (match (length arg)) + (post-completion (company-lsp--post-completion arg)))) + +(defun company-lsp--client-capabilities () + "Return the extra client capabilities supported by company-lsp." + (when company-lsp-enable-snippet + '(:textDocument (:completion (:completionItem (:snippetSupport t)))))) + +(add-hook 'lsp-before-initialize-hook + (lambda () + (lsp-register-client-capabilities 'company-lsp #'company-lsp--client-capabilities))) + +(provide 'company-lsp) +;;; company-lsp.el ends here -- cgit 1.4.1