minad / corfu

:desert_island: corfu.el - COmpletion in Region FUnction
GNU General Public License v3.0
1.15k stars 43 forks source link

Getting completion in dap-ui-repl #322

Closed ReggieMarr closed 1 year ago

ReggieMarr commented 1 year ago

It would be awesome if corfu could provide completion within a dap repl similar to what is done by company with dap-ui-repl-company. I put some stuff together but as of yet I can't seem to get completion candidates to show up.

(defun python-completion-on-buffer (buffer text)
  "Return a list of possible completions for TEXT in BUFFER."
  (with-current-buffer buffer
    (let ((completion-ignore-case t)
          (python-shell-completion-native-use-try-completion nil)
          (python-shell-completion-native-disabled-interpreters '("jython" "ironpython")))
      (python-shell-get-completions text 2))))

(defun python-completion-at-point-on-buffer (buffer)
  "Show Python completions at point in BUFFER."
  (let* ((bounds (bounds-of-thing-at-point 'symbol))
         (beg (car bounds))
         (end (cdr bounds))
         (text (buffer-substring-no-properties beg end))
         (completions (python-completion-on-buffer buffer text)))
    (when completions
      (list beg end completions))))

(defun python-completion-at-point-on-buffer-from-debug ()
  "Show Python completions at point for the current frame's buffer in the current session."
  (let* ((cur-session (dap--cur-session))
         (frame (dab--debug-session-active-frame cur-session))
         (buffer-path (when frame (gethash "path" (gethash "source" frame)))))
    (if buffer-path
        (let* ((buffer-name (file-name-nondirectory buffer-path))
               (bounds (bounds-of-thing-at-point 'symbol))
               (beg (car bounds))
               (end (cdr bounds))
               (text (buffer-substring-no-properties beg end))
               (completions (python-completion-on-buffer buffer-name text)))
          (when completions
            (list beg end completions)))
      (message "Unable to find active buffer"))))

(define-derived-mode dap-ui-repl-mode-capf comint-mode "DAP-REPL-CAPF"
  "Provide a REPL for the active debug session using CAPF."
  :group 'dap-ui
  :syntax-table emacs-lisp-mode-syntax-table
  (setq comint-prompt-regexp (concat "^" (regexp-quote dap-ui-repl-prompt))
        comint-input-sender 'dap-ui-input-sender
        comint-process-echoes nil)
  (add-hook 'completion-at-point-functions 'dap-ui-repl-capf-complete nil t)
  (unless (comint-check-proc (current-buffer))
    (insert dap-ui-repl-welcome)
    (start-process "dap-ui-repl-capf" (current-buffer) nil)
    (set-process-query-on-exit-flag (dap-ui-repl-process) nil)
    (goto-char (point-max))
    (set (make-local-variable 'comint-inhibit-carriage-motion) t)
    (comint-output-filter (dap-ui-repl-process) dap-ui-repl-prompt)
    (set-process-filter (dap-ui-repl-process) 'comint-output-filter)))

(defun dap-ui-repl-capf-complete ()
  "Completion function for dap-ui-repl-mode-capf."
  (when (eq major-mode 'dap-ui-repl-mode-capf)
    (let ((bounds (bounds-of-thing-at-point 'symbol)))
      (when bounds
        (list (car bounds) (cdr bounds) (dap-ui-repl--calculate-candidates)
              :annotation-function 'dap-ui-repl--annotate-capf)))))

(defun dap-ui-repl--annotate-capf (candidate)
  "Get annotation for CANDIDATE with CAPF."
  (concat " " (plist-get (text-properties-at 0 candidate) :type)))

(defun my/dap-capf-ui-repl ()
  "Start an adapter-specific REPL.
This could be used to evaluate JavaScript in a browser, to
evaluate python in the context of the debugee, ...."
  (interactive)
  (let ((repl-buf (get-buffer dap-ui--repl-buffer)))
    (unless repl-buf
      (with-current-buffer (get-buffer-create dap-ui--repl-buffer)
        (dap-ui-repl-mode-capf)
        (when (functionp 'corfu-mode)
          (corfu-mode 1))
        (setq-local lsp--buffer-workspaces (lsp-workspaces))
        (setq repl-buf (current-buffer))))
    (dap-ui--show-buffer repl-buf)))

(defun dap-ui-repl-capf-setup ()
  "Set up dap-ui-repl-capf for dap-ui-repl buffers."
  (add-hook 'completion-at-point-functions 'python-completion-at-point-on-buffer-from-debug nil t))

I'm able to get completion candidates in completion-in-region--data however corfu--candidates seems to be null. I got as far as debugging in corf--recompute and found that the var all is nil.

If I can get pointed in the right direction I'd appreciate it as I think this would be a really cool feature that would be useful when debugging python/other languages in dap.

minad commented 1 year ago

Did you try to use (cape-company-to-capf #'dap-ui-repl-company)? Add the resulting Capf to completion-at-point-functions. See https://github.com/minad/cape#company-adapter for more details.

ReggieMarr commented 1 year ago

Do you mean add something like this ?

(defun dap-ui-repl-capf-setup ()
  "Set up dap-ui-repl-capf for dap-ui-repl buffers."
  (setq-local completion-at-point-functions (cape-company-to-capf #'dap-ui-repl-company)))

That doesn't seem to work also doesn't that depend on company ? I would rather drop my use of company for corfu/cape

minad commented 1 year ago

Do you mean add something like this ?

Yes.

That doesn't seem to work also doesn't that depend on company ?

Not necessarily. You can write a backend which follows the Company protocol but doesn't depend on Company. See https://github.com/minad/cape#company-adapter.

But of course it would be better to write a dedicated Capf backend. From what I understand, your attempt from above doesn't work, since it seems that (dap-ui-repl--calculate-candidates) returns an async future which is Company-specific. It does not return a completion table.

My recommendation is to get this to work first with cape-company-to-capf. Install Corfu+Cape+Company+DAP. Then you can still continue from there and cut out Company.

I close this issue since it is not strictly a Corfu issue, but feel free to continue the discussion.

ReggieMarr commented 1 year ago

Do you have any insight into how completion-at-point-functions are normally set on an per-mode basis? In my experience I've used the add-hook function but it seems that might not be applicable here.

ReggieMarr commented 1 year ago

@minad so I think this is a corfu issue. I'm using the following config:

(defun setup-dap-ui-repl-completion ()
  (setq-local completion-at-point-functions
              (list (cape-company-to-capf 'dap-ui-repl-company))))
    (add-hook! 'dap-ui-repl-mode-hook #'setup-dap-ui-repl-completion)

and now I find the completions-at-point-functions

completion-at-point-functions is a variable defined in
minibuffer.el.gz.

Value in #<buffer *dap-ui-repl*>
(#[0 "\3009\203\0\303\300!\204\0\3041\0\305\300\306\307#0\210\202\0\210\3009\203I\0\310\300\n\"\204I\0\300\311!\210\312\300\313\307#\210\300\n\236\211\203?\0\211\307\241\266\202I\0\300\307B\211\262\nB\210\314\300\315\"\211\205\\\0\211;\203Z\0\211\202\\\0\211\242\211\205\323\0`\211GZ\314\300\316\"\306C\314\300\317\"\203x\0\320\202y\0\321\322\323 \324\325\300$\314\300\326#\203\224\0\327\202\231\0\301\206\231\0\330$\331\300\332\314\300\333\"?%!\334\335\336\243\337\324\340\300\"\341\324\342\300\"\343\324\344\300\"\345\324\346\300\"\347\324\350\300\"\351\324\352\300\"\353\324\354\300#\257\266\204\207"
    [dap-ui-repl-company nil cape--company-init fboundp
                         (error)
                         require nil t alist-get init put company-init cape--company-call prefix duplicates ignore-case completion-table-case-fold identity cape--table-with-properties cape--cached-table make-closure
                         #[257 "\302\303\300\304#\240\210\301\203\0\302\305\302\242!\240\210\302\242\207"
                               [V0 V1 V2 cape--company-call candidates delete-dups]
                               6 "\n\n(fn INPUT)"]
                         no-cache equal string-prefix-p :category :sort sorted :exclusive no :company-prefix-length :company-doc-buffer
                         #[257 "\301\300\302#\207"
                               [V0 cape--company-call doc-buffer]
                               5 "\n\n(fn X)"]
                         :company-location
                         #[257 "\301\300\302#\207"
                               [V0 cape--company-call location]
                               5 "\n\n(fn X)"]
                         :company-docsig
                         #[257 "\301\300\302#\207"
                               [V0 cape--company-call meta]
                               5 "\n\n(fn X)"]
                         :company-deprecated
                         #[257 "\301\300\302#\207"
                               [V0 cape--company-call deprecated]
                               5 "\n\n(fn X)"]
                         :company-kind
                         #[257 "\301\300\302#\207"
                               [V0 cape--company-call kind]
                               5 "\n\n(fn X)"]
                         :annotation-function
                         #[257 "\302\300\303#\211\205\0\304\305\306\307#)\266\203\203\0\207\310P\207"
                               [V0 inhibit-changing-match-data cape--company-call annotation "^[    ]" nil t string-match " "]
                               9 "\n\n(fn X)"]
                         :exit-function
                         #[514 "\302\300\303\301\242\235@\206\f\0#\207"
                               [V0 V1 cape--company-call post-completion]
                               7 "\n\n(fn X STATUS)"]]
    30])

But despite this I find I can only get completions if company mode is enabled. Could this be to do with dap-ui-repl--calculate-candidates returning an async future as you mentioned ? I'm not really sure how to go forwards from there.

minad commented 1 year ago

so I think this is a corfu issue.

No, I am pretty sure it is not. It is more likely a problem in cape-company-to-capf or a specific interaction problem of cape-company-to-capf and the dap backend.

Could this be to do with dap-ui-repl--calculate-candidates returning an async future as you mentioned ?

No, cape-company-to-capf also supports async futures.

ReggieMarr commented 1 year ago

It seems I was mistaken, for some reason the char count related to corfu-auto-prefix seems to be reset around a .. For example in the following python code:

import pandas as pd

#with corfu-auto-prefix set to 2 this will return completion candidates in a python buffer but not in a dap-ui-repl buffer
pd.
#with corfu-auto-prefix set to 2 this will return completion candidates in both.
pd.Da