minad / corfu

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

TAB-and-Go completion: `(wrong-type-argument integer-or-marker-p nil)` #426

Closed alecStewart1 closed 7 months ago

alecStewart1 commented 7 months ago

Hello! I use the TAB-and-Go completion, and, for awhile, I was a smidge annoyed that when I cycled through the candidates and pressed "SPC" that the candidate wasn't inserted and instead a space was added as a separator.

I release that, by default, corfu-separator is "SPC" or key 32. Also for Orderless I have it's orderless-component-separator as orderless-escapable-split-on-space:

Even if you want to split on spaces you might want to be able to escape those spaces or to enclose space in double quotes (as in shell argument parsing). For backslash-escaped spaces set orderless-component-separator to the function orderless-escapable-split-on-space; for shell-like double-quotable space, set it to the standard Emacs function split-string-and-unquote.

Only just now did I see the following in the wiki:

In TAB-and-Go style, if a candidate is selected, then further input commits that candidate unless it is the first one. In order that the first candidate is also committed on further input, add this to your configuration:

(dolist (c (list (cons "SPC" " ")
                 (cons "." ".")
                 (cons "," ",")
                 (cons ":" ":")
                 (cons ")" ")")
                 (cons "}" "}")
                 (cons "]" "]")))
  (define-key corfu-map (kbd (car c)) `(lambda ()
                                         (interactive)
                                         (corfu-insert)
                                         (insert ,(cdr c)))))

However this gives me the above mentioned error whenever I try to insert any of the characters after tabbing to any candidate in the completion list. Here's the debug output:

Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
  corfu--replace(nil nil #("add-text-properties" 0 4 (face orderless-match-face-0)))
  #f(compiled-function (status) #<bytecode 0x56df4685c728d0>)(finished)
  apply(#f(compiled-function (status) #<bytecode 0x56df4685c728d0>) finished)
  #f(compiled-function (&rest args) #<bytecode 0x1acc3e26028a96dd>)(finished)
  apply(#f(compiled-function (&rest args) #<bytecode 0x1acc3e26028a96dd>) finished)
  corfu--insert(finished)
  corfu-insert()
  (lambda nil (interactive) (corfu-insert) (insert " "))()
  funcall-interactively((lambda nil (interactive) (corfu-insert) (insert " ")))
  command-execute((lambda nil (interactive) (corfu-insert) (insert " ")))

Also I realize that my completion-styles is not just basic. To help, here's the bits from my configuration for orderless and corfu:

;;;; Orderless
;;;;

(use-package orderless
  :preface
  (defun orderless:fast-dispatch (word index total)
    "Dispatch orderless on a WORD where INDEX is 0 and TOTAL is at least 1 character.
Potentially speeds up completion a little bit."
    (and (= index 0) (= total 1) (length< word 1)
         `(orderless-regexp . ,(concat "^" (regexp-quote word)))))
  :config
  (orderless-define-completion-style orderless-fast
    (orderless-style-dispatchers '(orderless:fast-dispatch))
    (orderless-matching-styles '(orderless-literal orderless-initialism orderless-prefixes)))
  (setq orderless-component-separator #'orderless-escapable-split-on-space
        completion-styles '(orderless-fast basic)
        completion-category-defaults nil
        completion-category-overrides
        '((file (styles . (orderless-fast partial-completion)))
          (buffer (styles . (orderless-fast partial-completion)))
          (info-menu (styles . (orderless-fast partial-completion)))))
  (set-face-attribute 'completions-first-difference nil :inherit nil))

;;;; Corfu
;;;;

(use-package corfu
  :hook (after-init . global-corfu-mode)
  :general (:keymaps 'corfu-map
            "TAB"       #'corfu-next
            "<tab>"     #'corfu-next
            [tab]       #'corfu-next
            "C-n"       #'corfu-next
            "S-TAB"     #'corfu-previous
            "<S-tab>"   #'corfu-previous
            "<backtab>" #'corfu-previous
            [backtab]   #'corfu-previous
            "C-p"       #'corfu-previous
            ;; "RET"       #'ignore
            ;; "<return>"  #'ignore
            ;; [return]    #'ignore
            ;; "SPC"       #'corfu-insert-separator
            ;; "<space>"   #'corfu-insert-separator
            ;; [space]     #'corfu-insert-separator
            "M-d"       #'corfu-show-documentation
            "M-l"       #'corfu-show-location)
  :preface
  (defun corfu:in-minibuffer ()
    "Allow for Corfu in the minibuffer."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input))
      (setq-local corfu-auto nil)
      (corfu-mode +1)))

  (defun corfu:send-shell (&rest _)
    "Send completion candidates when inside comint/shell."
    (cond
     ((and (derived-mode-p 'eshell-mode) (fboundp 'eshell-send-input))
      (eshell-send-input))
     ((and (derived-mode-p 'comint-mode)  (fboundp 'comint-send-input))
      (comint-send-input))))

  (defun corfu:insert-shell-filter (&optional _)
    "Insert completion candidate and send when inside comint/eshell."
    (when (derived-mode-p 'eshell-mode 'comint-mode)
      (lambda ()
        (interactive)
        (corfu-insert)
        (corfu:send-shell))))
  :custom
  (corfu-cycle t)
  (corfu-auto t)
  (corfu-auto-delay 0.5)
  (corfu-auto-prefix 2)
  (corfu-preselect 'prompt)
  (corfu-quit-no-match t)
  (corfu-scroll-margin 6)
  (corfu-echo-documentation nil)
  (corfu-popupinfo-delay (cons 0.5 0.5))
  :config
  (add-hook 'minibuffer-setup-hook #'corfu:in-minibuffer 1)

  (dolist (c (list (cons "SPC" " ")
                   (cons "." ".")
                   (cons "," ",")
                   (cons ":" ":")
                   (cons ")" ")")
                   (cons "}" "}")
                   (cons "]" "]")))
    (define-key corfu-map (kbd (car c)) `(lambda ()
                                           (interactive)
                                           (corfu-insert)
                                           (insert ,(cdr c)))))
  (corfu-echo-mode +1)
  (corfu-history-mode +1)
  (corfu-popupinfo-mode +1))

So the expected behavior is:

  1. Corfu give a list of candidates after whatever is set for corfu-auto-delay
  2. "TAB" / "<tab>" / [tab] will cycle through the candidates and "select" but not "insert" them
  3. "SPC" / "<space>" / [space] / " " will insert the currently selected candidate, or any of the above listed keys
  4. One can continue typing
  5. Process is repeated

Maybe this is just a documentation thing, or a I-can't-actually-read thing.