lsp-completion.el (38178B)
1 ;;; lsp-completion.el --- LSP completion -*- lexical-binding: t; -*- 2 ;; 3 ;; Copyright (C) 2020 emacs-lsp maintainers 4 ;; 5 ;; This program is free software; you can redistribute it and/or modify 6 ;; it under the terms of the GNU General Public License as published by 7 ;; the Free Software Foundation, either version 3 of the License, or 8 ;; (at your option) any later version. 9 10 ;; This program is distributed in the hope that it will be useful, 11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 ;; GNU General Public License for more details. 14 15 ;; You should have received a copy of the GNU General Public License 16 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 17 ;; 18 ;;; Commentary: 19 ;; 20 ;; LSP completion 21 ;; 22 ;;; Code: 23 24 (require 'lsp-mode) 25 26 (defgroup lsp-completion nil 27 "LSP support for completion." 28 :prefix "lsp-completion-" 29 :group 'lsp-mode 30 :tag "LSP Completion") 31 32 ;;;###autoload 33 (define-obsolete-variable-alias 'lsp-prefer-capf 34 'lsp-completion-provider "lsp-mode 7.0.1") 35 36 (defcustom lsp-completion-provider :capf 37 "The completion backend provider." 38 :type '(choice 39 (const :tag "Use company-capf" :capf) 40 (const :tag "None" :none)) 41 :group 'lsp-completion 42 :package-version '(lsp-mode . "7.0.1")) 43 44 ;;;###autoload 45 (define-obsolete-variable-alias 'lsp-enable-completion-at-point 46 'lsp-completion-enable "lsp-mode 7.0.1") 47 48 (defcustom lsp-completion-enable t 49 "Enable `completion-at-point' integration." 50 :type 'boolean 51 :group 'lsp-completion) 52 53 (defcustom lsp-completion-enable-additional-text-edit t 54 "Whether or not to apply additional text edit when performing completion. 55 56 If set to non-nil, `lsp-mode' will apply additional text edits 57 from the server. Otherwise, the additional text edits are 58 ignored." 59 :type 'boolean 60 :group 'lsp-completion 61 :package-version '(lsp-mode . "6.3.2")) 62 63 (defcustom lsp-completion-show-kind t 64 "Whether or not to show kind of completion candidates." 65 :type 'boolean 66 :group 'lsp-completion 67 :package-version '(lsp-mode . "7.0.1")) 68 69 (defcustom lsp-completion-show-detail t 70 "Whether or not to show detail of completion candidates." 71 :type 'boolean 72 :group 'lsp-completion) 73 74 (defcustom lsp-completion-show-label-description t 75 "Whether or not to show description of completion candidates." 76 :type 'boolean 77 :group 'lsp-completion 78 :package-version '(lsp-mode . "9.0.0")) 79 80 (defcustom lsp-completion-no-cache nil 81 "Whether or not caching the returned completions from server." 82 :type 'boolean 83 :group 'lsp-completion 84 :package-version '(lsp-mode . "7.0.1")) 85 86 (defcustom lsp-completion-filter-on-incomplete t 87 "Whether or not filter incomplete results." 88 :type 'boolean 89 :group 'lsp-completion 90 :package-version '(lsp-mode . "7.0.1")) 91 92 (defcustom lsp-completion-sort-initial-results t 93 "Whether or not filter initial results from server." 94 :type 'boolean 95 :group 'lsp-completion 96 :package-version '(lsp-mode . "8.0.0")) 97 98 (defcustom lsp-completion-use-last-result t 99 "Temporarily use last server result when interrupted by keyboard. 100 This will help minimize popup flickering issue in `company-mode'." 101 :type 'boolean 102 :group 'lsp-completion 103 :package-version '(lsp-mode . "8.0.0")) 104 105 (defcustom lsp-completion-default-behaviour :replace 106 "Default behaviour of `InsertReplaceEdit'." 107 :type '(choice 108 (const :tag "Default completion inserts" :insert) 109 (const :tag "Default completion replaces" :replace)) 110 :group 'lsp-completion 111 :package-version '(lsp-mode . "8.0.0")) 112 113 (defconst lsp-completion--item-kind 114 [nil 115 "Text" 116 "Method" 117 "Function" 118 "Constructor" 119 "Field" 120 "Variable" 121 "Class" 122 "Interface" 123 "Module" 124 "Property" 125 "Unit" 126 "Value" 127 "Enum" 128 "Keyword" 129 "Snippet" 130 "Color" 131 "File" 132 "Reference" 133 "Folder" 134 "EnumMember" 135 "Constant" 136 "Struct" 137 "Event" 138 "Operator" 139 "TypeParameter"]) 140 141 (defvar yas-indent-line) 142 (defvar company-backends) 143 (defvar company-abort-on-unique-match) 144 145 (defvar lsp-completion--no-reordering nil 146 "Dont do client-side reordering completion items when set.") 147 148 (declare-function company-mode "ext:company") 149 (declare-function yas-expand-snippet "ext:yasnippet") 150 151 (defun lsp-doc-buffer (&optional string) 152 "Return doc for STRING." 153 (with-current-buffer (get-buffer-create "*lsp-documentation*") 154 (erase-buffer) 155 (fundamental-mode) 156 (when string 157 (save-excursion 158 (insert string) 159 (visual-line-mode))) 160 (current-buffer))) 161 162 (defun lsp-falsy? (val) 163 "Non-nil if VAL is falsy." 164 ;; https://developer.mozilla.org/en-US/docs/Glossary/Falsy 165 (or (not val) (equal val "") (equal val 0))) 166 167 (cl-defun lsp-completion--make-item (item &key markers prefix) 168 "Make completion item from lsp ITEM and with MARKERS and PREFIX." 169 (-let (((&CompletionItem :label 170 :sort-text? 171 :_emacsStartPoint start-point) 172 item)) 173 (propertize label 174 'lsp-completion-item item 175 'lsp-sort-text sort-text? 176 'lsp-completion-start-point start-point 177 'lsp-completion-markers markers 178 'lsp-completion-prefix prefix))) 179 180 (defun lsp-completion--fix-resolve-data (item) 181 "Patch `CompletionItem' ITEM for rust-analyzer otherwise resolve will fail. 182 See #2675" 183 (let ((data (lsp:completion-item-data? item))) 184 (when (lsp-member? data :import_for_trait_assoc_item) 185 (unless (lsp-get data :import_for_trait_assoc_item) 186 (lsp-put data :import_for_trait_assoc_item :json-false))))) 187 188 (defun lsp-completion--resolve (item) 189 "Resolve completion ITEM. 190 ITEM can be string or a CompletionItem" 191 (cl-assert item nil "Completion item must not be nil") 192 (-let (((completion-item . resolved) 193 (pcase item 194 ((pred stringp) (cons (get-text-property 0 'lsp-completion-item item) 195 (get-text-property 0 'lsp-completion-resolved item))) 196 (_ (cons item nil))))) 197 (if resolved item 198 (lsp-completion--fix-resolve-data completion-item) 199 (setq completion-item 200 (or (ignore-errors 201 (when (lsp-feature? "completionItem/resolve") 202 (lsp-request "completionItem/resolve" 203 (lsp-delete (lsp-copy completion-item) :_emacsStartPoint)))) 204 completion-item)) 205 (pcase item 206 ((pred stringp) 207 (let ((len (length item))) 208 (put-text-property 0 len 'lsp-completion-item completion-item item) 209 (put-text-property 0 len 'lsp-completion-resolved t item) 210 item)) 211 (_ completion-item))))) 212 213 (defun lsp-completion--resolve-async (item callback &optional cleanup-fn) 214 "Resolve completion ITEM asynchronously with CALLBACK. 215 The CLEANUP-FN will be called to cleanup." 216 (cl-assert item nil "Completion item must not be nil") 217 (-let (((completion-item . resolved) 218 (pcase item 219 ((pred stringp) (cons (get-text-property 0 'lsp-completion-item item) 220 (get-text-property 0 'lsp-completion-resolved item))) 221 (_ (cons item nil))))) 222 (ignore-errors 223 (if (and (lsp-feature? "completionItem/resolve") (not resolved)) 224 (progn 225 (lsp-completion--fix-resolve-data completion-item) 226 (lsp-request-async "completionItem/resolve" 227 (lsp-delete (lsp-copy completion-item) :_emacsStartPoint) 228 (lambda (completion-item) 229 (when (stringp item) 230 (let ((len (length item))) 231 (put-text-property 0 len 'lsp-completion-item completion-item item) 232 (put-text-property 0 len 'lsp-completion-resolved t item) 233 item)) 234 (funcall callback completion-item) 235 (when cleanup-fn (funcall cleanup-fn))) 236 :error-handler (lambda (err) 237 (when cleanup-fn (funcall cleanup-fn)) 238 (error (lsp:json-error-message err))) 239 :cancel-handler cleanup-fn 240 :mode 'alive)) 241 (funcall callback completion-item) 242 (when cleanup-fn (funcall cleanup-fn)))))) 243 244 (defun lsp-completion--annotate (item) 245 "Annotate ITEM detail." 246 (-let (((completion-item &as &CompletionItem :detail? :kind? :label-details?) 247 (get-text-property 0 'lsp-completion-item item))) 248 (lsp-completion--resolve-async item #'ignore) 249 250 (concat (when (and lsp-completion-show-detail detail?) 251 (concat " " (s-replace "\r" "" detail?))) 252 (when (and lsp-completion-show-label-description label-details?) 253 (when-let ((description (and label-details? (lsp:label-details-description label-details?)))) 254 (format " %s" description))) 255 (when lsp-completion-show-kind 256 (when-let ((kind-name (and kind? (aref lsp-completion--item-kind kind?)))) 257 (format " (%s)" kind-name)))))) 258 259 (defun lsp-completion--looking-back-trigger-characterp (trigger-characters) 260 "Return character if text before point match any of the TRIGGER-CHARACTERS." 261 (unless (= (point) (line-beginning-position)) 262 (seq-some 263 (lambda (trigger-char) 264 (and (equal (buffer-substring-no-properties (- (point) (length trigger-char)) (point)) 265 trigger-char) 266 trigger-char)) 267 trigger-characters))) 268 269 (defvar lsp-completion--cache nil 270 "Cached candidates for completion at point function. 271 In the form of plist (prefix-pos items :lsp-items :prefix ...). 272 When the completion is incomplete, `items' contains value of :incomplete.") 273 274 (defvar lsp-completion--last-result nil 275 "Last completion result.") 276 277 (defun lsp-completion--clear-cache (&optional keep-last-result) 278 "Clear completion caches. 279 KEEP-LAST-RESULT if specified." 280 (-some-> lsp-completion--cache 281 (cddr) 282 (plist-get :markers) 283 (cl-second) 284 (set-marker nil)) 285 (setq lsp-completion--cache nil) 286 (unless keep-last-result (setq lsp-completion--last-result nil))) 287 288 (lsp-defun lsp-completion--guess-prefix ((item &as &CompletionItem :text-edit?)) 289 "Guess ITEM's prefix start point according to following heuristics: 290 - If `textEdit' exists, use insertion range start as prefix start point. 291 - Else, find the point before current point is longest prefix match of 292 `insertText' or `label'. And: 293 - The character before prefix is not word constitute 294 Return `nil' when fails to guess prefix." 295 (cond 296 ((lsp-insert-replace-edit? text-edit?) 297 (lsp--position-to-point (lsp:range-start (lsp:insert-replace-edit-insert text-edit?)))) 298 (text-edit? 299 (lsp--position-to-point (lsp:range-start (lsp:text-edit-range text-edit?)))) 300 (t 301 (-let* (((&CompletionItem :label :insert-text?) item) 302 (text (or (unless (lsp-falsy? insert-text?) insert-text?) label)) 303 (point (point)) 304 (start (max 1 (- point (length text)))) 305 (char-before (char-before start)) 306 start-point) 307 (while (and (< start point) (not start-point)) 308 (unless (or (and char-before (equal (char-syntax char-before) ?w)) 309 (not (string-prefix-p (buffer-substring-no-properties start point) 310 text))) 311 (setq start-point start)) 312 (cl-incf start) 313 (setq char-before (char-before start))) 314 start-point)))) 315 316 (defun lsp-completion--to-internal (items) 317 "Convert ITEMS into internal form." 318 (--> items 319 (-map (-lambda ((item &as &CompletionItem 320 :label 321 :filter-text? 322 :_emacsStartPoint start-point 323 :score?)) 324 `( :label ,(or (unless (lsp-falsy? filter-text?) filter-text?) label) 325 :item ,item 326 :start-point ,start-point 327 :score ,score?)) 328 it))) 329 330 (cl-defun lsp-completion--filter-candidates (items &key 331 lsp-items 332 markers 333 prefix 334 &allow-other-keys) 335 "List all possible completions in cached ITEMS with their prefixes. 336 We can pass LSP-ITEMS, which will be used when there's no cache. 337 The MARKERS and PREFIX value will be attached to each candidate." 338 (lsp--while-no-input 339 (->> 340 (if items 341 (--> (let (queries fuz-queries) 342 (-keep (-lambda ((cand &as &plist :label :start-point :score)) 343 (let* ((query (or (plist-get queries start-point) 344 (let ((s (buffer-substring-no-properties 345 start-point (point)))) 346 (setq queries (plist-put queries start-point s)) 347 s))) 348 (fuz-query (or (plist-get fuz-queries start-point) 349 (let ((s (lsp-completion--regex-fuz query))) 350 (setq fuz-queries 351 (plist-put fuz-queries start-point s)) 352 s))) 353 (label-len (length label))) 354 (when (string-match fuz-query label) 355 (put-text-property 0 label-len 'match-data (match-data) label) 356 (plist-put cand 357 :sort-score 358 (* (or (lsp-completion--fuz-score query label) 1e-05) 359 (or score 0.001))) 360 cand))) 361 items)) 362 (if lsp-completion--no-reordering 363 it 364 (sort it (lambda (o1 o2) 365 (> (plist-get o1 :sort-score) 366 (plist-get o2 :sort-score))))) 367 ;; TODO: pass additional function to sort the candidates 368 (-map (-rpartial #'plist-get :item) it)) 369 lsp-items) 370 (-map (lambda (item) (lsp-completion--make-item item 371 :markers markers 372 :prefix prefix)))))) 373 374 (defconst lsp-completion--kind->symbol 375 '((1 . text) 376 (2 . method) 377 (3 . function) 378 (4 . constructor) 379 (5 . field) 380 (6 . variable) 381 (7 . class) 382 (8 . interface) 383 (9 . module) 384 (10 . property) 385 (11 . unit) 386 (12 . value) 387 (13 . enum) 388 (14 . keyword) 389 (15 . snippet) 390 (16 . color) 391 (17 . file) 392 (18 . reference) 393 (19 . folder) 394 (20 . enum-member) 395 (21 . constant) 396 (22 . struct) 397 (23 . event) 398 (24 . operator) 399 (25 . type-parameter))) 400 401 (defun lsp-completion--candidate-kind (item) 402 "Return ITEM's kind." 403 (alist-get (lsp:completion-item-kind? (get-text-property 0 'lsp-completion-item item)) 404 lsp-completion--kind->symbol)) 405 406 (defun lsp-completion--candidate-deprecated (item) 407 "Return if ITEM is deprecated." 408 (let ((completion-item (get-text-property 0 'lsp-completion-item item))) 409 (or (lsp:completion-item-deprecated? completion-item) 410 (seq-position (lsp:completion-item-tags? completion-item) 411 lsp/completion-item-tag-deprecated)))) 412 413 (defun lsp-completion--company-match (candidate) 414 "Return highlight of typed prefix inside CANDIDATE." 415 (if-let ((md (cddr (plist-get (text-properties-at 0 candidate) 'match-data)))) 416 (let (matches start end) 417 (while (progn (setq start (pop md) end (pop md)) 418 (and start end)) 419 (setq matches (nconc matches `((,start . ,end))))) 420 matches) 421 (let* ((prefix (downcase 422 (buffer-substring-no-properties 423 ;; Put a safe guard to prevent staled cache from setting a wrong start point #4192 424 (max (line-beginning-position) 425 (plist-get (text-properties-at 0 candidate) 'lsp-completion-start-point)) 426 (point)))) 427 (prefix-len (length prefix)) 428 (prefix-pos 0) 429 (label (downcase candidate)) 430 (label-len (length label)) 431 (label-pos 0) 432 matches start) 433 (while (and (not matches) 434 (< prefix-pos prefix-len)) 435 (while (and (< prefix-pos prefix-len) 436 (< label-pos label-len)) 437 (if (equal (aref prefix prefix-pos) (aref label label-pos)) 438 (progn 439 (unless start (setq start label-pos)) 440 (cl-incf prefix-pos)) 441 (when start 442 (setq matches (nconc matches `((,start . ,label-pos)))) 443 (setq start nil))) 444 (cl-incf label-pos)) 445 (when start (setq matches (nconc matches `((,start . ,label-pos))))) 446 ;; Search again when the whole prefix is not matched 447 (when (< prefix-pos prefix-len) 448 (setq matches nil)) 449 ;; Start search from next offset of prefix to find a match with label 450 (unless matches 451 (cl-incf prefix-pos) 452 (setq label-pos 0))) 453 matches))) 454 455 (defun lsp-completion--get-documentation (item) 456 "Get doc comment for completion ITEM." 457 (-some->> item 458 (lsp-completion--resolve) 459 (get-text-property 0 'lsp-completion-item) 460 (lsp:completion-item-documentation?) 461 (lsp--render-element))) 462 463 (defun lsp-completion--get-context (trigger-characters same-session?) 464 "Get completion context with provided TRIGGER-CHARACTERS and SAME-SESSION?." 465 (let* ((triggered-by-char non-essential) 466 (trigger-char (when triggered-by-char 467 (lsp-completion--looking-back-trigger-characterp 468 trigger-characters))) 469 (trigger-kind (cond 470 (trigger-char 471 lsp/completion-trigger-kind-trigger-character) 472 ((and same-session? 473 (equal (cl-second lsp-completion--cache) :incomplete)) 474 lsp/completion-trigger-kind-trigger-for-incomplete-completions) 475 (t lsp/completion-trigger-kind-invoked)))) 476 (apply #'lsp-make-completion-context 477 (nconc 478 `(:trigger-kind ,trigger-kind) 479 (when trigger-char 480 `(:trigger-character? ,trigger-char)))))) 481 482 (defun lsp-completion--sort-completions (completions) 483 "Sort COMPLETIONS." 484 (sort 485 completions 486 (-lambda ((&CompletionItem :sort-text? sort-text-left :label label-left) 487 (&CompletionItem :sort-text? sort-text-right :label label-right)) 488 (if (equal sort-text-left sort-text-right) 489 (string-lessp label-left label-right) 490 (string-lessp sort-text-left sort-text-right))))) 491 492 ;;;###autoload 493 (defun lsp-completion-at-point () 494 "Get lsp completions." 495 (when (or (--some (lsp--client-completion-in-comments? (lsp--workspace-client it)) 496 (lsp-workspaces)) 497 (not (nth 4 (syntax-ppss)))) 498 (let* ((trigger-chars (-> (lsp--capability-for-method "textDocument/completion") 499 (lsp:completion-options-trigger-characters?))) 500 (bounds-start (or (cl-first (bounds-of-thing-at-point 'symbol)) 501 (point))) 502 result done? 503 (candidates 504 (lambda () 505 (lsp--catch 'input 506 (let ((lsp--throw-on-input lsp-completion-use-last-result) 507 (same-session? (and lsp-completion--cache 508 ;; Special case for empty prefix and empty result 509 (or (cl-second lsp-completion--cache) 510 (not (string-empty-p 511 (plist-get (cddr lsp-completion--cache) :prefix)))) 512 (equal (cl-first lsp-completion--cache) bounds-start) 513 (s-prefix? 514 (plist-get (cddr lsp-completion--cache) :prefix) 515 (buffer-substring-no-properties bounds-start (point)))))) 516 (cond 517 ((or done? result) result) 518 ((and (not lsp-completion-no-cache) 519 same-session? 520 (listp (cl-second lsp-completion--cache))) 521 (setf result (apply #'lsp-completion--filter-candidates 522 (cdr lsp-completion--cache)))) 523 (t 524 (-let* ((resp (lsp-request-while-no-input 525 "textDocument/completion" 526 (plist-put (lsp--text-document-position-params) 527 :context (lsp-completion--get-context trigger-chars same-session?)))) 528 (completed (and resp 529 (not (and (lsp-completion-list? resp) 530 (lsp:completion-list-is-incomplete resp))))) 531 (items (lsp--while-no-input 532 (--> (cond 533 ((lsp-completion-list? resp) 534 (lsp:completion-list-items resp)) 535 (t resp)) 536 (if (or completed 537 (seq-some #'lsp:completion-item-sort-text? it)) 538 (lsp-completion--sort-completions it) 539 it) 540 (-map (lambda (item) 541 (lsp-put item 542 :_emacsStartPoint 543 (or (lsp-completion--guess-prefix item) 544 bounds-start))) 545 it)))) 546 (markers (list bounds-start (copy-marker (point) t))) 547 (prefix (buffer-substring-no-properties bounds-start (point))) 548 (lsp-completion--no-reordering (not lsp-completion-sort-initial-results))) 549 (lsp-completion--clear-cache same-session?) 550 (setf done? completed 551 lsp-completion--cache (list bounds-start 552 (cond 553 ((and done? (not (seq-empty-p items))) 554 (lsp-completion--to-internal items)) 555 ((not done?) :incomplete)) 556 :lsp-items nil 557 :markers markers 558 :prefix prefix) 559 result (lsp-completion--filter-candidates 560 (cond (done? 561 (cl-second lsp-completion--cache)) 562 (lsp-completion-filter-on-incomplete 563 (lsp-completion--to-internal items))) 564 :lsp-items items 565 :markers markers 566 :prefix prefix)))))) 567 (:interrupted lsp-completion--last-result) 568 (`,res (setq lsp-completion--last-result res)))))) 569 (list 570 bounds-start 571 (point) 572 (lambda (probe pred action) 573 (cond 574 ((eq action 'metadata) 575 '(metadata (category . lsp-capf) 576 (display-sort-function . identity) 577 (cycle-sort-function . identity))) 578 ((eq (car-safe action) 'boundaries) nil) 579 (t 580 (complete-with-action action (funcall candidates) probe pred)))) 581 :annotation-function #'lsp-completion--annotate 582 :company-kind #'lsp-completion--candidate-kind 583 :company-deprecated #'lsp-completion--candidate-deprecated 584 :company-require-match 'never 585 :company-prefix-length 586 (save-excursion 587 (let ( 588 ;; 2 is a heuristic number to make sure we look futher back than 589 ;; the bounds-start, which can be different from the actual start 590 ;; of the symbol 591 (bounds-left (max (line-beginning-position) (- bounds-start 2))) 592 triggered-by-char?) 593 (while (and (> (point) bounds-left) 594 (not (equal (char-after) ?\s)) 595 (not triggered-by-char?)) 596 (setq triggered-by-char? (lsp-completion--looking-back-trigger-characterp trigger-chars)) 597 (goto-char (1- (point)))) 598 (and triggered-by-char? t))) 599 :company-match #'lsp-completion--company-match 600 :company-doc-buffer (-compose #'lsp-doc-buffer 601 #'lsp-completion--get-documentation) 602 :exit-function 603 (-rpartial #'lsp-completion--exit-fn candidates))))) 604 605 (defun lsp-completion--find-workspace (server-id) 606 (--first (eq (lsp--client-server-id (lsp--workspace-client it)) server-id) 607 (lsp-workspaces))) 608 609 (defun lsp-completion--exit-fn (candidate _status &optional candidates) 610 "Exit function of `completion-at-point'. 611 CANDIDATE is the selected completion item. 612 Others: CANDIDATES" 613 (unwind-protect 614 (-let* ((candidate (if (plist-member (text-properties-at 0 candidate) 615 'lsp-completion-item) 616 candidate 617 (cl-find candidate (funcall candidates) :test #'equal))) 618 (candidate 619 ;; see #3498 typescript-language-server does not provide the 620 ;; proper insertText without resolving. 621 (if (lsp-completion--find-workspace 'ts-ls) 622 (lsp-completion--resolve candidate) 623 candidate)) 624 ((&plist 'lsp-completion-item item 625 'lsp-completion-start-point start-point 626 'lsp-completion-markers markers 627 'lsp-completion-resolved resolved 628 'lsp-completion-prefix prefix) 629 (text-properties-at 0 candidate)) 630 ((&CompletionItem? :label :insert-text? :text-edit? :insert-text-format? 631 :additional-text-edits? :insert-text-mode? :command?) 632 item)) 633 (cond 634 (text-edit? 635 (apply #'delete-region markers) 636 (insert prefix) 637 (pcase text-edit? 638 ((lsp-interface TextEdit) (lsp--apply-text-edit text-edit?)) 639 ((lsp-interface InsertReplaceEdit :insert :replace :new-text) 640 (lsp--apply-text-edit 641 (lsp-make-text-edit 642 :new-text new-text 643 :range (if (or (and current-prefix-arg (eq lsp-completion-default-behaviour :replace)) 644 (and (not current-prefix-arg) (eq lsp-completion-default-behaviour :insert))) 645 insert 646 replace)))))) 647 ((or (unless (lsp-falsy? insert-text?) insert-text?) label) 648 (apply #'delete-region markers) 649 (insert prefix) 650 (delete-region start-point (point)) 651 (insert (or (unless (lsp-falsy? insert-text?) insert-text?) label)))) 652 653 (lsp--indent-lines start-point (point) insert-text-mode?) 654 (when (equal insert-text-format? lsp/insert-text-format-snippet) 655 (lsp--expand-snippet (buffer-substring start-point (point)) 656 start-point 657 (point))) 658 659 (when lsp-completion-enable-additional-text-edit 660 (if (or resolved 661 (not (seq-empty-p additional-text-edits?))) 662 (lsp--apply-text-edits additional-text-edits? 'completion) 663 (-let [(callback cleanup-fn) (lsp--create-apply-text-edits-handlers)] 664 (lsp-completion--resolve-async 665 item 666 (-compose callback #'lsp:completion-item-additional-text-edits?) 667 cleanup-fn)))) 668 669 (if (or resolved command?) 670 (when command? (lsp--execute-command command?)) 671 (lsp-completion--resolve-async 672 item 673 (-lambda ((&CompletionItem? :command?)) 674 (when command? (lsp--execute-command command?))))) 675 676 (when (and (or 677 (equal lsp-signature-auto-activate t) 678 (memq :after-completion lsp-signature-auto-activate) 679 (and (memq :on-trigger-char lsp-signature-auto-activate) 680 (-when-let ((&SignatureHelpOptions? :trigger-characters?) 681 (lsp--capability :signatureHelpProvider)) 682 (lsp-completion--looking-back-trigger-characterp 683 trigger-characters?)))) 684 (lsp-feature? "textDocument/signatureHelp")) 685 (lsp-signature-activate)) 686 687 (setq-local lsp-inhibit-lsp-hooks nil)) 688 (lsp-completion--clear-cache))) 689 690 (defun lsp-completion--regex-fuz (str) 691 "Build a regex sequence from STR. Insert .* between each char." 692 (apply #'concat 693 (cl-mapcar 694 #'concat 695 (cons "" (cdr (seq-map (lambda (c) (format "[^%c]*" c)) str))) 696 (seq-map (lambda (c) 697 (format "\\(%s\\)" (regexp-quote (char-to-string c)))) 698 str)))) 699 700 (defun lsp-completion--fuz-score (query str) 701 "Calculate fuzzy score for STR with query QUERY. 702 The return is nil or in range of (0, inf)." 703 (-when-let* ((md (cddr (or (get-text-property 0 'match-data str) 704 (let ((re (lsp-completion--regex-fuz query))) 705 (when (string-match re str) 706 (match-data)))))) 707 (start (pop md)) 708 (len (length str)) 709 ;; To understand how this works, consider these bad ascii(tm) 710 ;; diagrams showing how the pattern "foo" flex-matches 711 ;; "fabrobazo", "fbarbazoo" and "barfoobaz": 712 713 ;; f abr o baz o 714 ;; + --- + --- + 715 716 ;; f barbaz oo 717 ;; + ------ ++ 718 719 ;; bar foo baz 720 ;; --- +++ --- 721 722 ;; "+" indicates parts where the pattern matched. A "hole" in 723 ;; the middle of the string is indicated by "-". Note that there 724 ;; are no "holes" near the edges of the string. The completion 725 ;; score is a number bound by ]0..1]: the higher the better and 726 ;; only a perfect match (pattern equals string) will have score 727 ;; 1. The formula takes the form of a quotient. For the 728 ;; numerator, we use the number of +, i.e. the length of the 729 ;; pattern. For the denominator, it first computes 730 ;; 731 ;; hole_i_contrib = 1 + (Li-1)^1.05 for first hole 732 ;; hole_i_contrib = 1 + (Li-1)^0.25 for hole i of length Li 733 ;; 734 ;; The final value for the denominator is then given by: 735 ;; 736 ;; (SUM_across_i(hole_i_contrib) + 1) 737 ;; 738 (score-numerator 0) 739 (score-denominator 0) 740 (last-b -1) 741 (q-ind 0) 742 (update-score 743 (lambda (a b) 744 "Update score variables given match range (A B)." 745 (setq score-numerator (+ score-numerator (- b a))) 746 (unless (= a len) 747 ;; case mismatch will be pushed to near next rank 748 (unless (equal (aref query q-ind) (aref str a)) 749 (cl-incf a 0.9)) 750 (setq score-denominator 751 (+ score-denominator 752 (if (= a last-b) 0 753 (+ 1 (* (if (< 0 (- a last-b 1)) 1 -1) 754 (expt (abs (- a last-b 1)) 755 ;; Give a higher score for match near start 756 (if (eq last-b -1) 0.75 0.25)))))))) 757 (setq last-b b)))) 758 (while md 759 (funcall update-score start (cl-first md)) 760 ;; Due to the way completion regex is constructed, `(eq end (+ start 1))` 761 (cl-incf q-ind) 762 (pop md) 763 (setq start (pop md))) 764 (unless (zerop len) 765 (/ score-numerator (1+ score-denominator) 1.0)))) 766 767 768 ;;;###autoload 769 (defun lsp-completion--enable () 770 "Enable LSP completion support." 771 (when (and lsp-completion-enable 772 (lsp-feature? "textDocument/completion")) 773 (lsp-completion-mode 1))) 774 775 (defun lsp-completion--disable () 776 "Disable LSP completion support." 777 (lsp-completion-mode -1)) 778 779 (defun lsp-completion-passthrough-try-completion (string table pred point) 780 (let* ((completion-ignore-case t) 781 (try (completion-basic-try-completion string table pred point)) 782 (newstr (car try)) 783 (newpoint (cdr try)) 784 (beforepoint (and try (substring newstr 0 newpoint)))) 785 (if (and beforepoint 786 (string-prefix-p 787 beforepoint 788 (try-completion "" table pred) 789 t)) 790 try 791 (cons string point)))) 792 793 (defun lsp-completion-passthrough-all-completions (_string table pred _point) 794 "Passthrough all completions from TABLE with PRED." 795 (defvar completion-lazy-hilit-fn) 796 (when (bound-and-true-p completion-lazy-hilit) 797 (setq completion-lazy-hilit-fn 798 (lambda (candidate) 799 (->> candidate 800 lsp-completion--company-match 801 (mapc (-lambda ((start . end)) 802 (put-text-property start end 'face 'completions-common-part candidate)))) 803 candidate))) 804 (all-completions "" table pred)) 805 806 ;;;###autoload 807 (define-minor-mode lsp-completion-mode 808 "Toggle LSP completion support." 809 :group 'lsp-completion 810 :global nil 811 :lighter "" 812 (let ((completion-started-fn (lambda (&rest _) 813 (setq-local lsp-inhibit-lsp-hooks t))) 814 (after-completion-fn (lambda (result) 815 (when (stringp result) 816 (lsp-completion--clear-cache)) 817 (setq-local lsp-inhibit-lsp-hooks nil)))) 818 (cond 819 (lsp-completion-mode 820 (make-local-variable 'completion-at-point-functions) 821 ;; Ensure that `lsp-completion-at-point' the first CAPF to be tried, 822 ;; unless user has put it elsewhere in the list by their own 823 (add-to-list 'completion-at-point-functions #'lsp-completion-at-point) 824 (make-local-variable 'completion-category-defaults) 825 (setf (alist-get 'lsp-capf completion-category-defaults) '((styles . (lsp-passthrough)))) 826 (make-local-variable 'completion-styles-alist) 827 (setf (alist-get 'lsp-passthrough completion-styles-alist) 828 '(lsp-completion-passthrough-try-completion 829 lsp-completion-passthrough-all-completions 830 "Passthrough completion.")) 831 832 (cond 833 ((equal lsp-completion-provider :none)) 834 ((and (not (equal lsp-completion-provider :none)) 835 (fboundp 'company-mode)) 836 (setq-local company-abort-on-unique-match nil) 837 (company-mode 1) 838 (setq-local company-backends (cl-adjoin 'company-capf company-backends :test #'equal))) 839 (t 840 (lsp--warn "Unable to autoconfigure company-mode."))) 841 842 (when (bound-and-true-p company-mode) 843 (add-hook 'company-completion-started-hook 844 completion-started-fn 845 nil 846 t) 847 (add-hook 'company-after-completion-hook 848 after-completion-fn 849 nil 850 t)) 851 (add-hook 'lsp-unconfigure-hook #'lsp-completion--disable nil t)) 852 (t 853 (remove-hook 'completion-at-point-functions #'lsp-completion-at-point t) 854 (setq-local completion-category-defaults 855 (cl-remove 'lsp-capf completion-category-defaults :key #'cl-first)) 856 (setq-local completion-styles-alist 857 (cl-remove 'lsp-passthrough completion-styles-alist :key #'cl-first)) 858 (remove-hook 'lsp-unconfigure-hook #'lsp-completion--disable t) 859 (when (featurep 'company) 860 (remove-hook 'company-completion-started-hook 861 completion-started-fn 862 t) 863 (remove-hook 'company-after-completion-hook 864 after-completion-fn 865 t)))))) 866 867 ;;;###autoload 868 (add-hook 'lsp-configure-hook (lambda () 869 (when (and lsp-auto-configure 870 lsp-completion-enable) 871 (lsp-completion--enable)))) 872 873 (lsp-consistency-check lsp-completion) 874 875 (provide 'lsp-completion) 876 ;;; lsp-completion.el ends here