Function: eglot-completion-at-point

eglot-completion-at-point is a byte-compiled function defined in eglot.el.gz.

Signature

(eglot-completion-at-point)

Documentation

Eglot's completion-at-point function.

Source Code

;; Defined in /usr/src/emacs/lisp/progmodes/eglot.el.gz
(cl-defun eglot-completion-at-point (&aux completion-capability)
  "Eglot's `completion-at-point' function."
  ;; Commit logs for this function help understand what's going on.
  (setq completion-capability (eglot-server-capable :completionProvider))
  (unless completion-capability (cl-return-from eglot-completion-at-point))
  (let* ((server (eglot--current-server-or-lose))
         (bounds (or (bounds-of-thing-at-point 'symbol)
                     (cons (point) (point))))
         (bounds-string (buffer-substring (car bounds) (cdr bounds)))
         (local-cache :none)
         (orig-pos (point))
         (resolved (make-hash-table)))
    (cl-labels
        ((sort-completions (completions)
           (cl-sort completions
                    #'string-lessp
                    :key (lambda (c)
                           (plist-get
                            (get-text-property 0 'eglot--lsp-item c)
                            :sortText))))
         (proxies ()
           (if (listp local-cache) local-cache
             (let* ((resp (eglot--request server
                                          :textDocument/completion
                                          (eglot--CompletionParams)
                                          :cancel-on-input t))
                    (items (append
                            (if (vectorp resp) resp (plist-get resp :items))
                            nil))
                    (cachep (and (listp resp) items
                                 eglot-cache-session-completions
                                 (eq (plist-get resp :isIncomplete) :json-false)))
                    (retval
                     (mapcar
                      (jsonrpc-lambda
                          (&rest item &key label insertText insertTextFormat
                                 textEdit &allow-other-keys)
                        (let ((proxy
                               ;; Snippet or textEdit, it's safe to
                               ;; display/insert the label since
                               ;; it'll be adjusted.  If no usable
                               ;; insertText at all, label is best,
                               ;; too.
                               (cond ((or (eql insertTextFormat 2)
                                          textEdit
                                          (null insertText)
                                          (string-empty-p insertText))
                                      (string-trim-left label))
                                     (t insertText))))
                          (unless (zerop (length proxy))
                            (put-text-property 0 1 'eglot--lsp-item item proxy))
                          proxy))
                      items)))
               ;; (trace-values "Requested" (length proxies) cachep bounds)
               (setq eglot--capf-session
                     (if cachep (list bounds retval resolved orig-pos
                                      bounds-string)
                       :none))
               (setq local-cache retval))))
         (ensure-resolved (lsp-comp &optional dont-cancel-on-input)
           ;; Maybe completion/resolve JSON object `lsp-comp' into
           ;; another JSON object, if at all possible.  Otherwise,
           ;; just return lsp-comp.
           (or (gethash lsp-comp resolved)
               (setf (gethash lsp-comp resolved)
                     (if (and (eglot-server-capable :completionProvider
                                                    :resolveProvider)
                              (plist-get lsp-comp :data))
                         (eglot--request server :completionItem/resolve
                                         lsp-comp :cancel-on-input
                                         (not dont-cancel-on-input)
                                         :immediate t)
                       lsp-comp)))))
      (when (and (consp eglot--capf-session)
                 (= (car bounds) (car (nth 0 eglot--capf-session)))
                 (>= (cdr bounds) (cdr (nth 0 eglot--capf-session))))
        (setq local-cache (nth 1 eglot--capf-session)
              resolved (nth 2 eglot--capf-session)
              orig-pos (nth 3 eglot--capf-session)
              bounds-string (nth 4 eglot--capf-session))
        ;; (trace-values "Recalling cache" (length local-cache) bounds orig-pos)
        )
      (list
       (car bounds)
       (cdr bounds)
       (lambda (pattern pred action)
         (cond
          ((eq action 'metadata)                         ; metadata
           `(metadata (category . eglot-capf)
                      (display-sort-function . ,#'sort-completions)))
          ((eq action 'lambda)                           ; test-completion
           (test-completion pattern (proxies)))
          ((eq (car-safe action) 'boundaries) nil)       ; boundaries
          ((null action)                                 ; try-completion
           (try-completion pattern (proxies)))
          ((eq action t)                                 ; all-completions
           (let ((comps (proxies)))
             (dolist (c comps) (eglot--dumb-flex pattern c completion-ignore-case))
             (all-completions
              ""
              ;; copy strings, as some older emacs
              ;; versions will destroy properties.
              (mapcar #'substring comps)
              (lambda (proxy)
                (let* ((item (get-text-property 0 'eglot--lsp-item proxy))
                       (filterText (plist-get item :filterText)))
                  (and (or (null pred) (funcall pred proxy))
                       (eglot--dumb-flex
                        pattern (or filterText proxy) completion-ignore-case)))))))))
       :annotation-function
       (lambda (proxy)
         (eglot--dbind ((CompletionItem) detail kind)
             (get-text-property 0 'eglot--lsp-item proxy)
           (let* ((detail (and (stringp detail)
                               (not (string= detail ""))
                               detail))
                  (annotation
                   (or detail
                       (cdr (assoc kind eglot--kind-names)))))
             (when annotation
               (concat " "
                       (propertize annotation
                                   'face 'font-lock-function-name-face))))))
       :company-kind
       ;; Associate each lsp-item with a lsp-kind symbol.
       (lambda (proxy)
         (when-let* ((lsp-item (get-text-property 0 'eglot--lsp-item proxy))
                     (kind (alist-get (plist-get lsp-item :kind)
                                      eglot--kind-names)))
           (pcase kind
             ("EnumMember" 'enum-member)
             ("TypeParameter" 'type-parameter)
             (_ (intern (downcase kind))))))
       :company-deprecated
       (lambda (proxy)
         (when-let* ((lsp-item (get-text-property 0 'eglot--lsp-item proxy)))
           (or (seq-contains-p (plist-get lsp-item :tags)
                               1)
               (eq t (plist-get lsp-item :deprecated)))))
       :company-docsig
       (lambda (proxy)
         (let ((detail (plist-get
                        (ensure-resolved (get-text-property 0 'eglot--lsp-item proxy))
                        :detail)))
           (when (and (stringp detail) (not (string= detail "")))
             (eglot--format-markup detail major-mode))))
       :company-doc-buffer
       (lambda (proxy)
         (let* ((resolved
                 (ensure-resolved (get-text-property 0 'eglot--lsp-item proxy)))
                (documentation
                 (plist-get resolved :documentation))
                (formatted (and documentation
                                (eglot--format-markup documentation))))
           (when formatted
             (with-current-buffer (get-buffer-create " *eglot doc*")
               (erase-buffer)
               (insert formatted)
               (current-buffer)))))
       :company-require-match 'never
       :company-prefix-length
       (save-excursion
         (goto-char (car bounds))
         (when (listp completion-capability)
           (looking-back
            (regexp-opt
             (cl-coerce (cl-getf completion-capability :triggerCharacters) 'list))
            (eglot--bol))))
       :exit-function
       (lambda (proxy status)
         (eglot--capf-session-flush)
         (when (memq status '(finished exact))
           ;; To assist in using this whole `completion-at-point'
           ;; function inside `completion-in-region', ensure the exit
           ;; function runs in the buffer where the completion was
           ;; triggered from.  This should probably be in Emacs itself.
           ;; (github#505)
           (with-current-buffer (if (minibufferp)
                                    (window-buffer (minibuffer-selected-window))
                                  (current-buffer))
             (eglot--dbind ((CompletionItem) insertTextFormat
                            insertText textEdit additionalTextEdits label)
                 (ensure-resolved
                  (or (get-text-property 0 'eglot--lsp-item proxy)
                      ;; When selecting from the *Completions*
                      ;; buffer, `proxy' won't have any properties.
                      ;; A lookup should fix that (github#148)
                      (get-text-property
                       0 'eglot--lsp-item
                       (cl-find proxy (proxies) :test #'string=)))
                  ;; Be sure to pass non-nil here since we don't want
                  ;; any quick typing after the soon-to-be-undone
                  ;; insertion to potentially cancel an essential
                  ;; resolution request (github#1474).
                  'dont-cancel-on-input)
               (let* ((snippet-fn (and (eql insertTextFormat 2)
                                       (eglot--snippet-expansion-fn)))
                      (apply-edit
                       (lambda (range text)
                         (pcase-let ((`(,beg . ,end)
                                      (eglot-range-region range)))
                           (delete-region beg end)
                           (goto-char beg)
                           (funcall (or snippet-fn #'insert) text)))))
                 (cond (textEdit
                        ;; Revert buffer back to state when the edit
                        ;; was obtained from server. If a `proxy'
                        ;; "bar" was obtained from a buffer with
                        ;; "foo.b", the LSP edit applies to that
                        ;; state, _not_ the current "foo.bar".
                        (delete-region orig-pos (point))
                        (insert (substring bounds-string (- orig-pos (car bounds))))
                        (eglot--dcase textEdit
                          (((TextEdit) range newText)
                           (funcall apply-edit range newText))
                          (((InsertReplaceEdit) newText replace)
                           (funcall apply-edit replace newText))))
                       (snippet-fn
                        ;; A snippet should be inserted, but using plain
                        ;; `insertText'.  This requires us to delete the
                        ;; whole completion, since `insertText' is the full
                        ;; completion's text.
                        (delete-region (- (point) (length proxy)) (point))
                        (funcall snippet-fn (or insertText label))))
                 (when (cl-plusp (length additionalTextEdits))
                   (eglot--apply-text-edits additionalTextEdits)))
               (eglot--signal-textDocument/didChange)))))))))