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
(defun eglot-completion-at-point ()
  "Eglot's `completion-at-point' function."
  ;; Commit logs for this function help understand what's going on.
  (when-let (completion-capability (eglot-server-capable :completionProvider))
    (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)))
           (sort-completions
            (lambda (completions)
              (cl-sort completions
                       #'string-lessp
                       :key (lambda (c)
                              (plist-get
                               (get-text-property 0 'eglot--lsp-item c)
                               :sortText)))))
           (metadata `(metadata (category . eglot-capf)
                                (display-sort-function . ,sort-completions)))
           (local-cache :none)
           (orig-pos (point))
           (resolved (make-hash-table))
           (proxies
            (lambda ()
              (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)))))
           (resolve-maybe
            ;; Maybe completion/resolve JSON object `lsp-comp' into
            ;; another JSON object, if at all possible.  Otherwise,
            ;; just return lsp-comp.
            (lambda (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 t
                                            :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
          ((eq action 'lambda)                           ; test-completion
           (test-completion pattern (funcall proxies)))
          ((eq (car-safe action) 'boundaries) nil)       ; boundaries
          ((null action)                                 ; try-completion
           (try-completion pattern (funcall proxies)))
          ((eq action t)                                 ; all-completions
           (let ((comps (funcall proxies)))
             (dolist (c comps) (eglot--dumb-flex pattern c completion-ignore-case))
             (all-completions
              ""
              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
       ;; FIXME: autoImportText is specific to the pyright language server
       (lambda (proxy)
         (when-let* ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy))
                     (data (plist-get (funcall resolve-maybe lsp-comp) :data))
                     (import-text (plist-get data :autoImportText)))
           import-text))
       :company-doc-buffer
       (lambda (proxy)
         (let* ((documentation
                 (let ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy)))
                   (plist-get (funcall resolve-maybe lsp-comp) :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)
                 (funcall
                  resolve-maybe
                  (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 (funcall proxies) :test #'string=))))
               (let ((snippet-fn (and (eql insertTextFormat 2)
                                      (eglot--snippet-expansion-fn))))
                 (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--dbind ((TextEdit) range newText) textEdit
                          (pcase-let ((`(,beg . ,end)
                                       (eglot-range-region range)))
                            (delete-region beg end)
                            (goto-char beg)
                            (funcall (or snippet-fn #'insert) 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)))))))))