aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2021-09-21 22:20:17 +0100
committerJoão Távora2021-09-27 01:07:11 +0100
commit90cbf0cb8d9959b94ba09f1faa0dcb50c8dbddbd (patch)
treee76f8770ee587a6cfc0f77bd20ec2ce2c199eca1
parent68d73eb154c745cbba7b3fd6a0a0a087d7c157da (diff)
downloademacs-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.el83
-rw-r--r--src/lread.c2
-rw-r--r--test/lisp/progmodes/elisp-mode-tests.el16
-rw-r--r--test/lisp/progmodes/elisp-resources/simple-shorthand-test.el2
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.
537If non-nil this variable is a hash-table holding information
538specific to the current state of the Elisp obarray. If the
539obarray changes by any means (interning or uninterning a symbol),
540the variable is immediately set to nil.")
541
542(defun elisp--completion-local-symbols ()
543 "Compute collections all Elisp symbols for completion purposes.
544The return value is compatible with the COLLECTION form described
545in `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'.
537If the context at point allows only a certain category of 585If 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)
4356Lisp_Object 4356Lisp_Object
4357intern_driver (Lisp_Object string, Lisp_Object obarray, Lisp_Object index) 4357intern_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)