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