diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 353877f60c2..59d9c346424 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3142,8 +3142,18 @@ eglot--dumb-allc (defun eglot--dumb-tryc (pat table pred point) (let ((probe (funcall table pat pred nil))) (cond ((eq probe t) t) - (probe (cons probe (length probe))) - (t (cons pat point))))) + (probe + (if (and (not (equal probe pat)) + (cl-every + (lambda (s) (string-prefix-p probe s completion-ignore-case)) + (funcall table pat pred t))) + (cons probe (length probe)) + (cons pat point))) + (t + ;; Match ignoring suffix: if there are any completions for + ;; the current prefix at least, keep the current input. + (and (funcall table (substring pat 0 point) pred t) + (cons pat point)))))) (add-to-list 'completion-category-defaults '(eglot-capf (styles eglot--dumb-flex))) (add-to-list 'completion-styles-alist '(eglot--dumb-flex eglot--dumb-tryc eglot--dumb-allc)) diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 534c47b2646..4f745b26b42 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -600,6 +600,20 @@ eglot-test-basic-completions (message (buffer-string)) (should (looking-back "fprintf.?"))))) +(ert-deftest eglot-test-common-prefix-completion () + "Test completion appending the common prefix." + (skip-unless (executable-find "clangd")) + (eglot--with-fixture + `(("project" . (("coiso.c" . + ,(concat "int foo_bar; int foo_bar_baz;" + "int main() {foo"))))) + (with-current-buffer + (eglot--find-file-noselect "project/coiso.c") + (eglot--wait-for-clangd) + (goto-char (point-max)) + (completion-at-point) + (should (looking-back "{foo_bar"))))) + (ert-deftest eglot-test-non-unique-completions () "Test completion resulting in 'Complete, but not unique'." (skip-unless (executable-find "clangd")) @@ -619,6 +633,55 @@ eglot-test-non-unique-completions (forward-line -1) (should (looking-at "Complete, but not unique"))))))) +(ert-deftest eglot-test-stop-completion-on-nonprefix () + "Test completion also resulting in 'Complete, but not unique'." + (skip-unless (executable-find "clangd")) + (eglot--with-fixture + `(("project" . (("coiso.c" . + ,(concat "int foot; int footer; int fo_obar;" + "int main() {foo"))))) + (with-current-buffer + (eglot--find-file-noselect "project/coiso.c") + (eglot--wait-for-clangd) + (goto-char (point-max)) + (completion-at-point) + (should (looking-back "foo"))))) + +(ert-deftest eglot-test-try-completion-nomatch () + "Test completion table with non-matching input, returning nil." + (skip-unless (executable-find "clangd")) + (eglot--with-fixture + `(("project" . (("coiso.c" . + ,(concat "int main() {abc"))))) + (with-current-buffer + (eglot--find-file-noselect "project/coiso.c") + (eglot--wait-for-clangd) + (goto-char (point-max)) + (should + (null + (completion-try-completion + "abc" + (nth 2 (eglot-completion-at-point)) nil 3)))))) + +(ert-deftest eglot-test-try-completion-inside-symbol () + "Test completion table inside symbol, with only prefix matching." + (skip-unless (executable-find "clangd")) + (eglot--with-fixture + `(("project" . (("coiso.c" . + ,(concat + "int foobar;" + "int main() {foo123"))))) + (with-current-buffer + (eglot--find-file-noselect "project/coiso.c") + (eglot--wait-for-clangd) + (goto-char (- (point-max) 3)) + (should + (equal + '("foo123" . 3) + (completion-try-completion + "foo123" + (nth 2 (eglot-completion-at-point)) nil 3)))))) + (ert-deftest eglot-test-basic-xref () "Test basic xref functionality in a clangd LSP." (skip-unless (executable-find "clangd"))