aboutsummaryrefslogtreecommitdiffstats
path: root/elpa/lsp-mode-20220505.630/lsp-completion.el
diff options
context:
space:
mode:
Diffstat (limited to 'elpa/lsp-mode-20220505.630/lsp-completion.el')
-rw-r--r--elpa/lsp-mode-20220505.630/lsp-completion.el818
1 files changed, 818 insertions, 0 deletions
diff --git a/elpa/lsp-mode-20220505.630/lsp-completion.el b/elpa/lsp-mode-20220505.630/lsp-completion.el
new file mode 100644
index 0000000..3fb1bbc
--- /dev/null
+++ b/elpa/lsp-mode-20220505.630/lsp-completion.el
@@ -0,0 +1,818 @@
+;;; lsp-completion.el --- LSP completion -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2020 emacs-lsp maintainers
+;;
+;; 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 <https://www.gnu.org/licenses/>.
+;;
+;;; Commentary:
+;;
+;; LSP completion
+;;
+;;; Code:
+
+(require 'lsp-mode)
+
+(defgroup lsp-completion nil
+ "LSP support for completion"
+ :prefix "lsp-completion-"
+ :group 'lsp-mode
+ :tag "LSP Completion")
+
+;;;###autoload
+(define-obsolete-variable-alias 'lsp-prefer-capf
+ 'lsp-completion-provider "lsp-mode 7.0.1")
+
+(defcustom lsp-completion-provider :capf
+ "The completion backend provider."
+ :type '(choice
+ (const :tag "Use company-capf" :capf)
+ (const :tag "None" :none))
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "7.0.1"))
+
+;;;###autoload
+(define-obsolete-variable-alias 'lsp-enable-completion-at-point
+ 'lsp-completion-enable "lsp-mode 7.0.1")
+
+(defcustom lsp-completion-enable t
+ "Enable `completion-at-point' integration."
+ :type 'boolean
+ :group 'lsp-completion)
+
+(defcustom lsp-completion-enable-additional-text-edit t
+ "Whether or not to apply additional text edit when performing completion.
+
+If set to non-nil, `lsp-mode' will apply additional text edits
+from the server. Otherwise, the additional text edits are
+ignored."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "6.3.2"))
+
+(defcustom lsp-completion-show-kind t
+ "Whether or not to show kind of completion candidates."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "7.0.1"))
+
+(defcustom lsp-completion-show-detail t
+ "Whether or not to show detail of completion candidates."
+ :type 'boolean
+ :group 'lsp-completion)
+
+(defcustom lsp-completion-show-label-description t
+ "Whether or not to show description of completion candidates."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "8.0.1"))
+
+(defcustom lsp-completion-no-cache nil
+ "Whether or not caching the returned completions from server."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "7.0.1"))
+
+(defcustom lsp-completion-filter-on-incomplete t
+ "Whether or not filter incomplete results."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "7.0.1"))
+
+(defcustom lsp-completion-sort-initial-results t
+ "Whether or not filter initial results from server."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "8.0.0"))
+
+(defcustom lsp-completion-use-last-result t
+ "Temporarily use last server result when interrupted by keyboard.
+This will help minimize popup flickering issue in `company-mode'."
+ :type 'boolean
+ :group 'lsp-completion
+ :package-version '(lsp-mode . "8.0.0"))
+
+(defconst lsp-completion--item-kind
+ [nil
+ "Text"
+ "Method"
+ "Function"
+ "Constructor"
+ "Field"
+ "Variable"
+ "Class"
+ "Interface"
+ "Module"
+ "Property"
+ "Unit"
+ "Value"
+ "Enum"
+ "Keyword"
+ "Snippet"
+ "Color"
+ "File"
+ "Reference"
+ "Folder"
+ "EnumMember"
+ "Constant"
+ "Struct"
+ "Event"
+ "Operator"
+ "TypeParameter"])
+
+(defvar yas-indent-line)
+(defvar company-backends)
+(defvar company-abort-on-unique-match)
+
+(defvar lsp-completion--no-reordering nil
+ "Dont do client-side reordering completion items when set.")
+
+(declare-function company-mode "ext:company")
+(declare-function yas-expand-snippet "ext:yasnippet")
+
+(defun lsp-doc-buffer (&optional string)
+ (with-current-buffer (get-buffer-create "*lsp-documentation*")
+ (erase-buffer)
+ (fundamental-mode)
+ (when string
+ (save-excursion
+ (insert string)
+ (visual-line-mode)))
+ (current-buffer)))
+
+(defun lsp-falsy? (val)
+ "Non-nil if VAL is falsy."
+ ;; https://developer.mozilla.org/en-US/docs/Glossary/Falsy
+ (or (not val) (equal val "") (equal val 0)))
+
+(cl-defun lsp-completion--make-item (item &key markers prefix)
+ "Make completion item from lsp ITEM and with MARKERS and PREFIX."
+ (-let (((&CompletionItem :label
+ :sort-text?
+ :_emacsStartPoint start-point)
+ item))
+ (propertize label
+ 'lsp-completion-item item
+ 'lsp-sort-text sort-text?
+ 'lsp-completion-start-point start-point
+ 'lsp-completion-markers markers
+ 'lsp-completion-prefix prefix)))
+
+(defun lsp-completion--annotate (item)
+ "Annotate ITEM detail."
+ (-let (((&CompletionItem :detail? :kind? :label-details?) (plist-get (text-properties-at 0 item)
+ 'lsp-completion-item)))
+ (concat (when (and lsp-completion-show-detail detail?)
+ (concat " " (s-replace "\r" "" detail?)))
+ (when (and lsp-completion-show-label-description label-details?)
+ (when-let ((description (and label-details? (lsp:label-details-description label-details?))))
+ (format " %s" description)))
+ (when lsp-completion-show-kind
+ (when-let ((kind-name (and kind? (aref lsp-completion--item-kind kind?))))
+ (format " (%s)" kind-name))))))
+
+(defun lsp-completion--looking-back-trigger-characterp (trigger-characters)
+ "Return trigger character if text before point match any of the TRIGGER-CHARACTERS."
+ (unless (= (point) (point-at-bol))
+ (seq-some
+ (lambda (trigger-char)
+ (and (equal (buffer-substring-no-properties (- (point) (length trigger-char)) (point))
+ trigger-char)
+ trigger-char))
+ trigger-characters)))
+
+(defvar lsp-completion--cache nil
+ "Cached candidates for completion at point function.
+In the form of plist (prefix-pos items :lsp-items :prefix ...).
+When the completion is incomplete, `items' contains value of :incomplete.")
+
+(defvar lsp-completion--last-result nil
+ "Last completion result.")
+
+(defun lsp-completion--clear-cache (&optional keep-last-result)
+ "Clear completion caches.
+KEEP-LAST-RESULT if specified."
+ (-some-> lsp-completion--cache
+ (cddr)
+ (plist-get :markers)
+ (cl-second)
+ (set-marker nil))
+ (setq lsp-completion--cache nil)
+ (unless keep-last-result (setq lsp-completion--last-result nil)))
+
+(defcustom lsp-completion-default-behaviour :replace
+ "Default behaviour of `InsertReplaceEdit'."
+ :type '(choice
+ (const :insert :tag "Default completion inserts")
+ (const :replace :tag "Default completion replaces"))
+ :group 'lsp-mode
+ :package-version '(lsp-mode . "8.0.0"))
+
+(lsp-defun lsp-completion--guess-prefix ((item &as &CompletionItem :text-edit?))
+ "Guess ITEM's prefix start point according to following heuristics:
+- If `textEdit' exists, use insertion range start as prefix start point.
+- Else, find the point before current point is longest prefix match of
+`insertText' or `label'. And:
+ - The character before prefix is not word constitute
+Return `nil' when fails to guess prefix."
+ (cond
+ ((lsp-insert-replace-edit? text-edit?)
+ (lsp--position-to-point (lsp:range-start (lsp:insert-replace-edit-insert text-edit?))))
+ (text-edit?
+ (lsp--position-to-point (lsp:range-start (lsp:text-edit-range text-edit?))))
+ (t
+ (-let* (((&CompletionItem :label :insert-text?) item)
+ (text (or (unless (lsp-falsy? insert-text?) insert-text?) label))
+ (point (point))
+ (start (max 1 (- point (length text))))
+ (char-before (char-before start))
+ start-point)
+ (while (and (< start point) (not start-point))
+ (unless (or (and char-before (equal (char-syntax char-before) ?w))
+ (not (string-prefix-p (buffer-substring-no-properties start point)
+ text)))
+ (setq start-point start))
+ (cl-incf start)
+ (setq char-before (char-before start)))
+ start-point))))
+
+(defun lsp-completion--to-internal (items)
+ "Convert ITEMS into internal form."
+ (--> items
+ (-map (-lambda ((item &as &CompletionItem
+ :label
+ :filter-text?
+ :_emacsStartPoint start-point
+ :score?))
+ `( :label ,(or (unless (lsp-falsy? filter-text?) filter-text?) label)
+ :item ,item
+ :start-point ,start-point
+ :score ,score?))
+ it)))
+
+(cl-defun lsp-completion--filter-candidates (items &key
+ lsp-items
+ markers
+ prefix
+ &allow-other-keys)
+ "List all possible completions in cached ITEMS with their prefixes.
+We can pass LSP-ITEMS, which will be used when there's no cache.
+The MARKERS and PREFIX value will be attached to each candidate."
+ (lsp--while-no-input
+ (->>
+ (if items
+ (-->
+ (let (queries fuz-queries)
+ (-keep (-lambda ((cand &as &plist :label :start-point :score))
+ (let* ((query (or (plist-get queries start-point)
+ (let ((s (buffer-substring-no-properties
+ start-point (point))))
+ (setq queries (plist-put queries start-point s))
+ s)))
+ (fuz-query (or (plist-get fuz-queries start-point)
+ (let ((s (lsp-completion--regex-fuz query)))
+ (setq fuz-queries
+ (plist-put fuz-queries start-point s))
+ s)))
+ (label-len (length label)))
+ (when (string-match fuz-query label)
+ (put-text-property 0 label-len 'match-data (match-data) label)
+ (plist-put cand
+ :sort-score
+ (* (or (lsp-completion--fuz-score query label) 1e-05)
+ (or score 0.001)))
+ cand)))
+ items))
+ (if lsp-completion--no-reordering
+ it
+ (sort it (lambda (o1 o2)
+ (> (plist-get o1 :sort-score)
+ (plist-get o2 :sort-score)))))
+ ;; TODO: pass additional function to sort the candidates
+ (-map (-rpartial #'plist-get :item) it))
+ lsp-items)
+ (-map (lambda (item) (lsp-completion--make-item item
+ :markers markers
+ :prefix prefix))))))
+
+(defconst lsp-completion--kind->symbol
+ '((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)
+ (19 . folder)
+ (20 . enum-member)
+ (21 . constant)
+ (22 . struct)
+ (23 . event)
+ (24 . operator)
+ (25 . type-parameter)))
+
+(defun lsp-completion--candidate-kind (item)
+ "Return ITEM's kind."
+ (alist-get (lsp:completion-item-kind? (get-text-property 0 'lsp-completion-item item))
+ lsp-completion--kind->symbol))
+
+(defun lsp-completion--candidate-deprecated (item)
+ "Return if ITEM is deprecated."
+ (let ((completion-item (get-text-property 0 'lsp-completion-item item)))
+ (or (lsp:completion-item-deprecated? completion-item)
+ (seq-position (lsp:completion-item-tags? completion-item)
+ lsp/completion-item-tag-deprecated))))
+
+(defun lsp-completion--company-match (candidate)
+ "Return highlight of typed prefix inside CANDIDATE."
+ (let* ((prefix (downcase
+ (buffer-substring-no-properties
+ (plist-get (text-properties-at 0 candidate) 'lsp-completion-start-point)
+ (point))))
+ (prefix-len (length prefix))
+ (prefix-pos 0)
+ (label (downcase candidate))
+ (label-len (length label))
+ (label-pos 0)
+ matches start)
+ (while (and (not matches)
+ (< prefix-pos prefix-len))
+ (while (and (< prefix-pos prefix-len)
+ (< label-pos label-len))
+ (if (equal (aref prefix prefix-pos) (aref label label-pos))
+ (progn
+ (unless start (setq start label-pos))
+ (cl-incf prefix-pos))
+ (when start
+ (setq matches (nconc matches `((,start . ,label-pos))))
+ (setq start nil)))
+ (cl-incf label-pos))
+ (when start (setq matches (nconc matches `((,start . ,label-pos)))))
+ ;; Search again when the whole prefix is not matched
+ (when (< prefix-pos prefix-len)
+ (setq matches nil))
+ ;; Start search from next offset of prefix to find a match with label
+ (unless matches
+ (cl-incf prefix-pos)
+ (setq label-pos 0)))
+ matches))
+
+(defun lsp-completion--get-documentation (item)
+ "Get doc comment for completion ITEM."
+ (unless (get-text-property 0 'lsp-completion-resolved item)
+ (let ((resolved-item
+ (-some->> item
+ (get-text-property 0 'lsp-completion-item)
+ (lsp-completion--resolve)))
+ (len (length item)))
+ (put-text-property 0 len 'lsp-completion-item resolved-item item)
+ (put-text-property 0 len 'lsp-completion-resolved t item)))
+ (-some->> item
+ (get-text-property 0 'lsp-completion-item)
+ (lsp:completion-item-documentation?)
+ (lsp--render-element)))
+
+(defun lsp-completion--get-context (trigger-characters)
+ "Get completion context with provided TRIGGER-CHARACTERS."
+ (let* ((triggered-by-char non-essential)
+ (trigger-char (when triggered-by-char
+ (lsp-completion--looking-back-trigger-characterp
+ trigger-characters)))
+ (trigger-kind (cond
+ (trigger-char
+ lsp/completion-trigger-kind-trigger-character)
+ ((equal (cl-second lsp-completion--cache) :incomplete)
+ lsp/completion-trigger-kind-trigger-for-incomplete-completions)
+ (t lsp/completion-trigger-kind-invoked))))
+ (apply #'lsp-make-completion-context
+ (nconc
+ `(:trigger-kind ,trigger-kind)
+ (when trigger-char
+ `(:trigger-character? ,trigger-char))))))
+
+(defun lsp-completion--sort-completions (completions)
+ "Sort COMPLETIONS."
+ (sort
+ completions
+ (-lambda ((&CompletionItem :sort-text? sort-text-left :label label-left)
+ (&CompletionItem :sort-text? sort-text-right :label label-right))
+ (if (equal sort-text-left sort-text-right)
+ (string-lessp label-left label-right)
+ (string-lessp sort-text-left sort-text-right)))))
+
+;;;###autoload
+(defun lsp-completion-at-point ()
+ "Get lsp completions."
+ (when (or (--some (lsp--client-completion-in-comments? (lsp--workspace-client it))
+ (lsp-workspaces))
+ (not (nth 4 (syntax-ppss))))
+ (let* ((trigger-chars (->> (lsp--server-capabilities)
+ (lsp:server-capabilities-completion-provider?)
+ (lsp:completion-options-trigger-characters?)))
+ (bounds-start (or (-some--> (cl-first (bounds-of-thing-at-point 'symbol))
+ (save-excursion
+ (ignore-errors
+ (goto-char (+ it 1))
+ (while (lsp-completion--looking-back-trigger-characterp
+ trigger-chars)
+ (cl-incf it)
+ (forward-char))
+ it)))
+ (point)))
+ result done?
+ (candidates
+ (lambda ()
+ (lsp--catch 'input
+ (let ((lsp--throw-on-input lsp-completion-use-last-result)
+ (same-session? (and lsp-completion--cache
+ ;; Special case for empty prefix and empty result
+ (or (cl-second lsp-completion--cache)
+ (not (string-empty-p
+ (plist-get (cddr lsp-completion--cache) :prefix))))
+ (equal (cl-first lsp-completion--cache) bounds-start)
+ (s-prefix?
+ (plist-get (cddr lsp-completion--cache) :prefix)
+ (buffer-substring-no-properties bounds-start (point))))))
+ (cond
+ ((or done? result) result)
+ ((and (not lsp-completion-no-cache)
+ same-session?
+ (listp (cl-second lsp-completion--cache)))
+ (setf result (apply #'lsp-completion--filter-candidates
+ (cdr lsp-completion--cache))))
+ (t
+ (-let* ((resp (lsp-request-while-no-input
+ "textDocument/completion"
+ (plist-put (lsp--text-document-position-params)
+ :context (lsp-completion--get-context trigger-chars))))
+ (completed (and resp
+ (not (and (lsp-completion-list? resp)
+ (lsp:completion-list-is-incomplete resp)))))
+ (items (lsp--while-no-input
+ (--> (cond
+ ((lsp-completion-list? resp)
+ (lsp:completion-list-items resp))
+ (t resp))
+ (if (or completed
+ (seq-some #'lsp:completion-item-sort-text? it))
+ (lsp-completion--sort-completions it)
+ it)
+ (-map (lambda (item)
+ (lsp-put item
+ :_emacsStartPoint
+ (or (lsp-completion--guess-prefix item)
+ bounds-start)))
+ it))))
+ (markers (list bounds-start (copy-marker (point) t)))
+ (prefix (buffer-substring-no-properties bounds-start (point)))
+ (lsp-completion--no-reordering (not lsp-completion-sort-initial-results)))
+ (lsp-completion--clear-cache same-session?)
+ (setf done? completed
+ lsp-completion--cache (list bounds-start
+ (cond
+ ((and done? (not (seq-empty-p items)))
+ (lsp-completion--to-internal items))
+ ((not done?) :incomplete))
+ :lsp-items nil
+ :markers markers
+ :prefix prefix)
+ result (lsp-completion--filter-candidates
+ (cond (done?
+ (cl-second lsp-completion--cache))
+ (lsp-completion-filter-on-incomplete
+ (lsp-completion--to-internal items)))
+ :lsp-items items
+ :markers markers
+ :prefix prefix))))))
+ (:interrupted lsp-completion--last-result)
+ (`,res (setq lsp-completion--last-result res))))))
+ (list
+ bounds-start
+ (point)
+ (lambda (probe pred action)
+ (cond
+ ;; metadata
+ ((equal action 'metadata)
+ `(metadata (category . lsp-capf)
+ (display-sort-function . identity)
+ (cycle-sort-function . identity)))
+ ;; boundaries
+ ((equal (car-safe action) 'boundaries) nil)
+ ;; try-completion
+ ((null action)
+ (when-let ((cands (funcall candidates)))
+ (if (cl-rest cands) probe (cl-first cands))))
+ ;; test-completion: not return exact match so that the selection will
+ ;; always be shown
+ ((equal action 'lambda) nil)
+ ;; retrieve candidates
+ ((equal action t)
+ (all-completions probe (funcall candidates) pred))))
+ :annotation-function #'lsp-completion--annotate
+ :company-kind #'lsp-completion--candidate-kind
+ :company-deprecated #'lsp-completion--candidate-deprecated
+ :company-require-match 'never
+ :company-prefix-length
+ (save-excursion
+ (goto-char bounds-start)
+ (and (lsp-completion--looking-back-trigger-characterp trigger-chars) t))
+ :company-match #'lsp-completion--company-match
+ :company-doc-buffer (-compose #'lsp-doc-buffer
+ #'lsp-completion--get-documentation)
+ :exit-function
+ (-rpartial #'lsp-completion--exit-fn candidates)))))
+
+(defun lsp-completion--exit-fn (candidate _status &optional candidates)
+ "Exit function of `completion-at-point'.
+CANDIDATE is the selected completion item.
+Others: CANDIDATES"
+ (unwind-protect
+ (-let* ((candidate (if (plist-member (text-properties-at 0 candidate)
+ 'lsp-completion-item)
+ candidate
+ (cl-find candidate (funcall candidates) :test #'equal)))
+ ((&plist 'lsp-completion-item item
+ 'lsp-completion-start-point start-point
+ 'lsp-completion-markers markers
+ 'lsp-completion-prefix prefix)
+ (text-properties-at 0 candidate))
+ ((&CompletionItem? :label :insert-text? :text-edit? :insert-text-format?
+ :additional-text-edits? :insert-text-mode? :command?)
+ item))
+ (cond
+ (text-edit?
+ (apply #'delete-region markers)
+ (insert prefix)
+ (pcase text-edit?
+ ((TextEdit) (lsp--apply-text-edit text-edit?))
+ ((InsertReplaceEdit :insert :replace :new-text)
+ (lsp--apply-text-edit
+ (lsp-make-text-edit
+ :new-text new-text
+ :range (if (or (and current-prefix-arg (eq lsp-completion-default-behaviour :replace))
+ (and (not current-prefix-arg) (eq lsp-completion-default-behaviour :insert)))
+ insert
+ replace))))))
+ ((or (unless (lsp-falsy? insert-text?) insert-text?) label)
+ (apply #'delete-region markers)
+ (insert prefix)
+ (delete-region start-point (point))
+ (insert (or (unless (lsp-falsy? insert-text?) insert-text?) label))))
+
+ (lsp--indent-lines start-point (point) insert-text-mode?)
+ (when (equal insert-text-format? lsp/insert-text-format-snippet)
+ (lsp--expand-snippet (buffer-substring start-point (point))
+ start-point
+ (point)))
+
+ (when lsp-completion-enable-additional-text-edit
+ (if (or (get-text-property 0 'lsp-completion-resolved candidate)
+ (not (seq-empty-p additional-text-edits?)))
+ (lsp--apply-text-edits additional-text-edits? 'completion)
+ (-let [(callback cleanup-fn) (lsp--create-apply-text-edits-handlers)]
+ (lsp-completion--resolve-async
+ item
+ (-compose callback #'lsp:completion-item-additional-text-edits?)
+ cleanup-fn))))
+
+ (if (or (get-text-property 0 'lsp-completion-resolved candidate)
+ command?)
+ (when command? (lsp--execute-command command?))
+ (lsp-completion--resolve-async
+ item
+ (-lambda ((&CompletionItem? :command?))
+ (when command? (lsp--execute-command command?)))))
+
+ (when (and (or
+ (equal lsp-signature-auto-activate t)
+ (memq :after-completion lsp-signature-auto-activate)
+ (and (memq :on-trigger-char lsp-signature-auto-activate)
+ (-when-let ((&SignatureHelpOptions? :trigger-characters?)
+ (lsp--capability :signatureHelpProvider))
+ (lsp-completion--looking-back-trigger-characterp
+ trigger-characters?))))
+ (lsp-feature? "textDocument/signatureHelp"))
+ (lsp-signature-activate))
+
+ (setq-local lsp-inhibit-lsp-hooks nil))
+ (lsp-completion--clear-cache)))
+
+(defun lsp-completion--regex-fuz (str)
+ "Build a regex sequence from STR. Insert .* between each char."
+ (apply #'concat
+ (cl-mapcar
+ #'concat
+ (cons "" (cdr (seq-map (lambda (c) (format "[^%c]*" c)) str)))
+ (seq-map (lambda (c)
+ (format "\\(%s\\)" (regexp-quote (char-to-string c))))
+ str))))
+
+(defun lsp-completion--fuz-score (query str)
+ "Calculate fuzzy score for STR with query QUERY.
+The return is nil or in range of (0, inf)."
+ (-when-let* ((md (cddr (or (get-text-property 0 'match-data str)
+ (let ((re (lsp-completion--regex-fuz query)))
+ (when (string-match re str)
+ (match-data))))))
+ (start (pop md))
+ (len (length str))
+ ;; To understand how this works, consider these bad ascii(tm)
+ ;; diagrams showing how the pattern "foo" flex-matches
+ ;; "fabrobazo", "fbarbazoo" and "barfoobaz":
+
+ ;; f abr o baz o
+ ;; + --- + --- +
+
+ ;; f barbaz oo
+ ;; + ------ ++
+
+ ;; bar foo baz
+ ;; --- +++ ---
+
+ ;; "+" indicates parts where the pattern matched. A "hole" in
+ ;; the middle of the string is indicated by "-". Note that there
+ ;; are no "holes" near the edges of the string. The completion
+ ;; score is a number bound by ]0..1]: the higher the better and
+ ;; only a perfect match (pattern equals string) will have score
+ ;; 1. The formula takes the form of a quotient. For the
+ ;; numerator, we use the number of +, i.e. the length of the
+ ;; pattern. For the denominator, it first computes
+ ;;
+ ;; hole_i_contrib = 1 + (Li-1)^1.05 for first hole
+ ;; hole_i_contrib = 1 + (Li-1)^0.25 for hole i of length Li
+ ;;
+ ;; The final value for the denominator is then given by:
+ ;;
+ ;; (SUM_across_i(hole_i_contrib) + 1)
+ ;;
+ (score-numerator 0)
+ (score-denominator 0)
+ (last-b -1)
+ (q-ind 0)
+ (update-score
+ (lambda (a b)
+ "Update score variables given match range (A B)."
+ (setq score-numerator (+ score-numerator (- b a)))
+ (unless (= a len)
+ ;; case mis-match will be pushed to near next rank
+ (unless (equal (aref query q-ind) (aref str a))
+ (cl-incf a 0.9))
+ (setq score-denominator
+ (+ score-denominator
+ (if (= a last-b) 0
+ (+ 1 (* (if (< 0 (- a last-b 1)) 1 -1)
+ (expt (abs (- a last-b 1))
+ ;; Give a higher score for match near start
+ (if (eq last-b -1) 0.75 0.25))))))))
+ (setq last-b b))))
+ (while md
+ (funcall update-score start (cl-first md))
+ ;; Due to the way completion regex is constructed, `(eq end (+ start 1))`
+ (cl-incf q-ind)
+ (pop md)
+ (setq start (pop md)))
+ (unless (zerop len)
+ (/ score-numerator (1+ score-denominator) 1.0))))
+
+(defun lsp-completion--fix-resolve-data (item)
+ ;; patch `CompletionItem' for rust-analyzer otherwise resolve will fail
+ ;; see #2675
+ (let ((data (lsp:completion-item-data? item)))
+ (when (lsp-member? data :import_for_trait_assoc_item)
+ (unless (lsp-get data :import_for_trait_assoc_item)
+ (lsp-put data :import_for_trait_assoc_item :json-false)))))
+
+(defun lsp-completion--resolve (item)
+ "Resolve completion ITEM."
+ (cl-assert item nil "Completion item must not be nil")
+ (lsp-completion--fix-resolve-data item)
+ (or (ignore-errors
+ (when (lsp-feature? "completionItem/resolve")
+ (lsp-request "completionItem/resolve" item)))
+ item))
+
+(defun lsp-completion--resolve-async (item callback &optional cleanup-fn)
+ "Resolve completion ITEM asynchronously with CALLBACK.
+The CLEANUP-FN will be called to cleanup."
+ (cl-assert item nil "Completion item must not be nil")
+ (lsp-completion--fix-resolve-data item)
+ (ignore-errors
+ (if (lsp-feature? "completionItem/resolve")
+ (lsp-request-async "completionItem/resolve" item
+ (lambda (result)
+ (funcall callback result)
+ (when cleanup-fn (funcall cleanup-fn)))
+ :error-handler (lambda (err)
+ (when cleanup-fn (funcall cleanup-fn))
+ (error (lsp:json-error-message err)))
+ :cancel-handler cleanup-fn
+ :mode 'alive)
+ (funcall callback item)
+ (when cleanup-fn (funcall cleanup-fn)))))
+
+
+;;;###autoload
+(defun lsp-completion--enable ()
+ "Enable LSP completion support."
+ (when (and lsp-completion-enable
+ (lsp-feature? "textDocument/completion"))
+ (lsp-completion-mode 1)))
+
+(defun lsp-completion--disable ()
+ "Disable LSP completion support."
+ (lsp-completion-mode -1))
+
+(defun lsp-completion-passthrough-all-completions (_string table pred _point)
+ "Like `completion-basic-all-completions' but have prefix ignored."
+ (completion-basic-all-completions "" table pred 0))
+
+;;;###autoload
+(define-minor-mode lsp-completion-mode
+ "Toggle LSP completion support."
+ :group 'lsp-completion
+ :global nil
+ :lighter ""
+ (let ((completion-started-fn (lambda (&rest _)
+ (setq-local lsp-inhibit-lsp-hooks t)))
+ (after-completion-fn (lambda (result)
+ (when (stringp result)
+ (lsp-completion--clear-cache))
+ (setq-local lsp-inhibit-lsp-hooks nil))))
+ (cond
+ (lsp-completion-mode
+ (setq-local completion-at-point-functions nil)
+ (add-hook 'completion-at-point-functions #'lsp-completion-at-point nil t)
+ (make-local-variable 'completion-category-defaults)
+ (setf (alist-get 'lsp-capf completion-category-defaults) '((styles . (lsp-passthrough))))
+ (make-local-variable 'completion-styles-alist)
+ (setf (alist-get 'lsp-passthrough completion-styles-alist)
+ '(completion-basic-try-completion
+ lsp-completion-passthrough-all-completions
+ "Passthrough completion."))
+
+ (cond
+ ((equal lsp-completion-provider :none))
+ ((and (not (equal lsp-completion-provider :none))
+ (fboundp 'company-mode))
+ (setq-local company-abort-on-unique-match nil)
+ (company-mode 1)
+ (setq-local company-backends (cl-adjoin 'company-capf company-backends :test #'equal)))
+ (t
+ (lsp--warn "Unable to autoconfigure company-mode.")))
+
+ (when (bound-and-true-p company-mode)
+ (add-hook 'company-completion-started-hook
+ completion-started-fn
+ nil
+ t)
+ (add-hook 'company-after-completion-hook
+ after-completion-fn
+ nil
+ t))
+ (add-hook 'lsp-unconfigure-hook #'lsp-completion--disable nil t))
+ (t
+ (remove-hook 'completion-at-point-functions #'lsp-completion-at-point t)
+ (setq-local completion-category-defaults
+ (cl-remove 'lsp-capf completion-category-defaults :key #'cl-first))
+ (setq-local completion-styles-alist
+ (cl-remove 'lsp-passthrough completion-styles-alist :key #'cl-first))
+ (remove-hook 'lsp-unconfigure-hook #'lsp-completion--disable t)
+ (when (featurep 'company)
+ (remove-hook 'company-completion-started-hook
+ completion-started-fn
+ t)
+ (remove-hook 'company-after-completion-hook
+ after-completion-fn
+ t))))))
+
+;;;###autoload
+(add-hook 'lsp-configure-hook (lambda ()
+ (when (and lsp-auto-configure
+ lsp-completion-enable)
+ (lsp-completion--enable))))
+
+(lsp-consistency-check lsp-completion)
+
+(provide 'lsp-completion)
+;;; lsp-completion.el ends here