diff options
| author | João Távora | 2021-09-21 22:20:17 +0100 |
|---|---|---|
| committer | João Távora | 2021-09-27 01:07:11 +0100 |
| commit | 90cbf0cb8d9959b94ba09f1faa0dcb50c8dbddbd (patch) | |
| tree | e76f8770ee587a6cfc0f77bd20ec2ce2c199eca1 | |
| parent | 68d73eb154c745cbba7b3fd6a0a0a087d7c157da (diff) | |
| download | emacs-90cbf0cb8d9959b94ba09f1faa0dcb50c8dbddbd.tar.gz emacs-90cbf0cb8d9959b94ba09f1faa0dcb50c8dbddbd.zip | |
Consider shorthands in Elisp's elisp-completion-at-point
Instead of referencing obarray directly, that function has to consider
a collection of completions which includes the shorthand versions of
some of the symbols. That collection changes from buffer to buffer,
depending on the choice of elisp-shorthands.
To make this process efficient, and avoid needless recalculation of
the above collection, a new obarray-specific cache was invented. The
Elisp variable obarray-cache is immediately nullified if something
touches the obarray.
* lisp/progmodes/elisp-mode.el : New helper.
(elisp-completion-at-point): Use new helpers.
(elisp--completion-local-symbols)
(elisp--fboundp-considering-shorthands)
(elisp--bboundp-considering-shorthands): New helpers
* src/lread.c (intern_driver): Nullify Qobarray_cache.
(syms_of_lread): Add Qobarray_cache.
* test/lisp/progmodes/elisp-mode-tests.el
(elisp-shorthand-completion-at-point): New test.
* test/lisp/progmodes/elisp-resources/simple-shorthand-test.el
(f-test-complete-me): New fixture.
| -rw-r--r-- | lisp/progmodes/elisp-mode.el | 83 | ||||
| -rw-r--r-- | src/lread.c | 2 | ||||
| -rw-r--r-- | test/lisp/progmodes/elisp-mode-tests.el | 16 | ||||
| -rw-r--r-- | test/lisp/progmodes/elisp-resources/simple-shorthand-test.el | 2 |
4 files changed, 87 insertions, 16 deletions
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 4a0abb74b3f..d2ea25d67b0 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el | |||
| @@ -532,6 +532,54 @@ It can be quoted, or be inside a quoted form." | |||
| 532 | 0)) | 532 | 0)) |
| 533 | ((facep sym) (find-definition-noselect sym 'defface))))) | 533 | ((facep sym) (find-definition-noselect sym 'defface))))) |
| 534 | 534 | ||
| 535 | (defvar obarray-cache nil | ||
| 536 | "Hash table of obarray-related cache, or nil. | ||
| 537 | If non-nil this variable is a hash-table holding information | ||
| 538 | specific to the current state of the Elisp obarray. If the | ||
| 539 | obarray changes by any means (interning or uninterning a symbol), | ||
| 540 | the variable is immediately set to nil.") | ||
| 541 | |||
| 542 | (defun elisp--completion-local-symbols () | ||
| 543 | "Compute collections all Elisp symbols for completion purposes. | ||
| 544 | The return value is compatible with the COLLECTION form described | ||
| 545 | in `completion-at-point-functions' (which see)." | ||
| 546 | (cl-flet ((obarray-plus-shorthands () | ||
| 547 | (let (retval) | ||
| 548 | (mapatoms | ||
| 549 | (lambda (s) | ||
| 550 | (push s retval) | ||
| 551 | (cl-loop | ||
| 552 | for (shorthand . longhand) in elisp-shorthands | ||
| 553 | for full-name = (symbol-name s) | ||
| 554 | when (string-prefix-p longhand full-name) | ||
| 555 | do (let ((sym (make-symbol | ||
| 556 | (concat shorthand | ||
| 557 | (substring full-name | ||
| 558 | (length longhand)))))) | ||
| 559 | (put sym 'shorthand t) | ||
| 560 | (push sym retval) | ||
| 561 | retval)))) | ||
| 562 | retval))) | ||
| 563 | (cond ((null elisp-shorthands) obarray) | ||
| 564 | ((and obarray-cache | ||
| 565 | (gethash (cons (current-buffer) elisp-shorthands) | ||
| 566 | obarray-cache))) | ||
| 567 | (obarray-cache | ||
| 568 | (puthash (cons (current-buffer) elisp-shorthands) | ||
| 569 | (obarray-plus-shorthands) | ||
| 570 | obarray-cache)) | ||
| 571 | (t | ||
| 572 | (setq obarray-cache (make-hash-table :test #'equal)) | ||
| 573 | (puthash (cons (current-buffer) elisp-shorthands) | ||
| 574 | (obarray-plus-shorthands) | ||
| 575 | obarray-cache))))) | ||
| 576 | |||
| 577 | (defun elisp--shorthand-aware-fboundp (sym) | ||
| 578 | (fboundp (intern-soft (symbol-name sym)))) | ||
| 579 | |||
| 580 | (defun elisp--shorthand-aware-boundp (sym) | ||
| 581 | (boundp (intern-soft (symbol-name sym)))) | ||
| 582 | |||
| 535 | (defun elisp-completion-at-point () | 583 | (defun elisp-completion-at-point () |
| 536 | "Function used for `completion-at-point-functions' in `emacs-lisp-mode'. | 584 | "Function used for `completion-at-point-functions' in `emacs-lisp-mode'. |
| 537 | If the context at point allows only a certain category of | 585 | If the context at point allows only a certain category of |
| @@ -579,24 +627,27 @@ functions are annotated with \"<f>\" via the | |||
| 579 | ;; the current form and use it to provide a more | 627 | ;; the current form and use it to provide a more |
| 580 | ;; specific completion table in more cases. | 628 | ;; specific completion table in more cases. |
| 581 | ((eq fun-sym 'ignore-error) | 629 | ((eq fun-sym 'ignore-error) |
| 582 | (list t obarray | 630 | (list t (elisp--completion-local-symbols) |
| 583 | :predicate (lambda (sym) | 631 | :predicate (lambda (sym) |
| 584 | (get sym 'error-conditions)))) | 632 | (get sym 'error-conditions)))) |
| 585 | ((elisp--expect-function-p beg) | 633 | ((elisp--expect-function-p beg) |
| 586 | (list nil obarray | 634 | (list nil (elisp--completion-local-symbols) |
| 587 | :predicate #'fboundp | 635 | :predicate |
| 636 | #'elisp--shorthand-aware-fboundp | ||
| 588 | :company-kind #'elisp--company-kind | 637 | :company-kind #'elisp--company-kind |
| 589 | :company-doc-buffer #'elisp--company-doc-buffer | 638 | :company-doc-buffer #'elisp--company-doc-buffer |
| 590 | :company-docsig #'elisp--company-doc-string | 639 | :company-docsig #'elisp--company-doc-string |
| 591 | :company-location #'elisp--company-location)) | 640 | :company-location #'elisp--company-location)) |
| 592 | (quoted | 641 | (quoted |
| 593 | (list nil obarray | 642 | (list nil (elisp--completion-local-symbols) |
| 594 | ;; Don't include all symbols (bug#16646). | 643 | ;; Don't include all symbols (bug#16646). |
| 595 | :predicate (lambda (sym) | 644 | :predicate (lambda (sym) |
| 596 | (or (boundp sym) | 645 | ;; shorthand-aware |
| 597 | (fboundp sym) | 646 | (let ((sym (intern-soft (symbol-name sym)))) |
| 598 | (featurep sym) | 647 | (or (boundp sym) |
| 599 | (symbol-plist sym))) | 648 | (fboundp sym) |
| 649 | (featurep sym) | ||
| 650 | (symbol-plist sym)))) | ||
| 600 | :annotation-function | 651 | :annotation-function |
| 601 | (lambda (str) (if (fboundp (intern-soft str)) " <f>")) | 652 | (lambda (str) (if (fboundp (intern-soft str)) " <f>")) |
| 602 | :company-kind #'elisp--company-kind | 653 | :company-kind #'elisp--company-kind |
| @@ -607,8 +658,8 @@ functions are annotated with \"<f>\" via the | |||
| 607 | (list nil (completion-table-merge | 658 | (list nil (completion-table-merge |
| 608 | elisp--local-variables-completion-table | 659 | elisp--local-variables-completion-table |
| 609 | (apply-partially #'completion-table-with-predicate | 660 | (apply-partially #'completion-table-with-predicate |
| 610 | obarray | 661 | (elisp--completion-local-symbols) |
| 611 | #'boundp | 662 | #'elisp--shorthand-aware-boundp |
| 612 | 'strict)) | 663 | 'strict)) |
| 613 | :company-kind | 664 | :company-kind |
| 614 | (lambda (s) | 665 | (lambda (s) |
| @@ -645,11 +696,11 @@ functions are annotated with \"<f>\" via the | |||
| 645 | (ignore-errors | 696 | (ignore-errors |
| 646 | (forward-sexp 2) | 697 | (forward-sexp 2) |
| 647 | (< (point) beg))))) | 698 | (< (point) beg))))) |
| 648 | (list t obarray | 699 | (list t (elisp--completion-local-symbols) |
| 649 | :predicate (lambda (sym) (get sym 'error-conditions)))) | 700 | :predicate (lambda (sym) (get sym 'error-conditions)))) |
| 650 | ;; `ignore-error' with a list CONDITION parameter. | 701 | ;; `ignore-error' with a list CONDITION parameter. |
| 651 | ('ignore-error | 702 | ('ignore-error |
| 652 | (list t obarray | 703 | (list t (elisp--completion-local-symbols) |
| 653 | :predicate (lambda (sym) | 704 | :predicate (lambda (sym) |
| 654 | (get sym 'error-conditions)))) | 705 | (get sym 'error-conditions)))) |
| 655 | ((and (or ?\( 'let 'let*) | 706 | ((and (or ?\( 'let 'let*) |
| @@ -659,14 +710,14 @@ functions are annotated with \"<f>\" via the | |||
| 659 | (up-list -1)) | 710 | (up-list -1)) |
| 660 | (forward-symbol -1) | 711 | (forward-symbol -1) |
| 661 | (looking-at "\\_<let\\*?\\_>")))) | 712 | (looking-at "\\_<let\\*?\\_>")))) |
| 662 | (list t obarray | 713 | (list t (elisp--completion-local-symbols) |
| 663 | :predicate #'boundp | 714 | :predicate #'elisp--shorthand-aware-boundp |
| 664 | :company-kind (lambda (_) 'variable) | 715 | :company-kind (lambda (_) 'variable) |
| 665 | :company-doc-buffer #'elisp--company-doc-buffer | 716 | :company-doc-buffer #'elisp--company-doc-buffer |
| 666 | :company-docsig #'elisp--company-doc-string | 717 | :company-docsig #'elisp--company-doc-string |
| 667 | :company-location #'elisp--company-location)) | 718 | :company-location #'elisp--company-location)) |
| 668 | (_ (list nil obarray | 719 | (_ (list nil (elisp--completion-local-symbols) |
| 669 | :predicate #'fboundp | 720 | :predicate #'elisp--shorthand-aware-fboundp |
| 670 | :company-kind #'elisp--company-kind | 721 | :company-kind #'elisp--company-kind |
| 671 | :company-doc-buffer #'elisp--company-doc-buffer | 722 | :company-doc-buffer #'elisp--company-doc-buffer |
| 672 | :company-docsig #'elisp--company-doc-string | 723 | :company-docsig #'elisp--company-doc-string |
diff --git a/src/lread.c b/src/lread.c index 4b7fcc2875b..51a7084821e 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -4356,6 +4356,7 @@ intern_sym (Lisp_Object sym, Lisp_Object obarray, Lisp_Object index) | |||
| 4356 | Lisp_Object | 4356 | Lisp_Object |
| 4357 | intern_driver (Lisp_Object string, Lisp_Object obarray, Lisp_Object index) | 4357 | intern_driver (Lisp_Object string, Lisp_Object obarray, Lisp_Object index) |
| 4358 | { | 4358 | { |
| 4359 | SET_SYMBOL_VAL (XSYMBOL (Qobarray_cache), Qnil); | ||
| 4359 | return intern_sym (Fmake_symbol (string), obarray, index); | 4360 | return intern_sym (Fmake_symbol (string), obarray, index); |
| 4360 | } | 4361 | } |
| 4361 | 4362 | ||
| @@ -5427,4 +5428,5 @@ that are loaded before your customizations are read! */); | |||
| 5427 | DEFVAR_LISP ("elisp-shorthands", Velisp_shorthands, | 5428 | DEFVAR_LISP ("elisp-shorthands", Velisp_shorthands, |
| 5428 | doc: /* Alist of known symbol name shorthands*/); | 5429 | doc: /* Alist of known symbol name shorthands*/); |
| 5429 | Velisp_shorthands = Qnil; | 5430 | Velisp_shorthands = Qnil; |
| 5431 | DEFSYM (Qobarray_cache, "obarray-cache"); | ||
| 5430 | } | 5432 | } |
diff --git a/test/lisp/progmodes/elisp-mode-tests.el b/test/lisp/progmodes/elisp-mode-tests.el index d5d3f336fac..9fe583d8cc3 100644 --- a/test/lisp/progmodes/elisp-mode-tests.el +++ b/test/lisp/progmodes/elisp-mode-tests.el | |||
| @@ -1080,5 +1080,21 @@ evaluation of BODY." | |||
| 1080 | (should (intern-soft "elisp--foo-test")) | 1080 | (should (intern-soft "elisp--foo-test")) |
| 1081 | (should-not (intern-soft "f-test")))) | 1081 | (should-not (intern-soft "f-test")))) |
| 1082 | 1082 | ||
| 1083 | (ert-deftest elisp-shorthand-completion-at-point () | ||
| 1084 | (let ((test-file (expand-file-name "simple-shorthand-test.el" | ||
| 1085 | elisp--test-resources-dir))) | ||
| 1086 | (load test-file) | ||
| 1087 | (with-current-buffer (find-file-noselect test-file) | ||
| 1088 | (revert-buffer t t) | ||
| 1089 | (goto-char (point-min)) | ||
| 1090 | (insert "f-test-compl") | ||
| 1091 | (completion-at-point) | ||
| 1092 | (goto-char (point-min)) | ||
| 1093 | (should (search-forward "f-test-complete-me" (line-end-position) t)) | ||
| 1094 | (goto-char (point-min)) | ||
| 1095 | (should (string= (symbol-name (read (current-buffer))) | ||
| 1096 | "elisp--foo-test-complete-me")) | ||
| 1097 | (revert-buffer t t)))) | ||
| 1098 | |||
| 1083 | (provide 'elisp-mode-tests) | 1099 | (provide 'elisp-mode-tests) |
| 1084 | ;;; elisp-mode-tests.el ends here | 1100 | ;;; elisp-mode-tests.el ends here |
diff --git a/test/lisp/progmodes/elisp-resources/simple-shorthand-test.el b/test/lisp/progmodes/elisp-resources/simple-shorthand-test.el index 5634926c6d2..cadcb4de89d 100644 --- a/test/lisp/progmodes/elisp-resources/simple-shorthand-test.el +++ b/test/lisp/progmodes/elisp-resources/simple-shorthand-test.el | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | (let ((elisp-shorthands '(("foo-" . "bar-")))) | 14 | (let ((elisp-shorthands '(("foo-" . "bar-")))) |
| 15 | (intern "foo-bar"))) | 15 | (intern "foo-bar"))) |
| 16 | 16 | ||
| 17 | (defvar f-test-complete-me 42) | ||
| 18 | |||
| 17 | (when nil | 19 | (when nil |
| 18 | (f-test3) | 20 | (f-test3) |
| 19 | (f-test2) | 21 | (f-test2) |