From: Mathias Dahl Date: Sat, 26 Sep 2020 19:03:58 +0000 (-0400) Subject: Abbrev suggestions helps users remember to use defined abbrevs X-Git-Tag: emacs-28.0.90~5864 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=f43d9d94aafcdfbcc5a10498333a28b3f8220fcf;p=emacs.git Abbrev suggestions helps users remember to use defined abbrevs * lisp/abbrev.el (abbrev-suggest): New defcustom. (abbrev-suggest-hint-threshold): New defcustom. (abbrev--suggest-get-active-tables-including-parents): New defun. (abbrev--suggest-get-active-abbrev-expansions): New defun. (abbrev--suggest-count-words): New defun. (abbrev--suggest-get-previous-words): New defun. (abbrev--suggest-above-threshold): New defun. (abbrev--suggest-saved-recommendations): New defvar. (abbrev--suggest-inform-user): New defun. (abbrev--suggest-shortest-abbrev): New defun. (abbrev--suggest-maybe-suggest): New defun. (abbrev--suggest-get-totals): New defun. (abbrev-suggest-show-report): New defun. (expand-abbrev): If the previous word was not an abbrev, maybe suggest an abbrev to the user. * doc/emacs/abbrevs.texi (Abbrev suggestions): New section. * etc/NEWS: Announce abbrev suggestions. --- diff --git a/doc/emacs/abbrevs.texi b/doc/emacs/abbrevs.texi index 21bf8c53325..0dda977833f 100644 --- a/doc/emacs/abbrevs.texi +++ b/doc/emacs/abbrevs.texi @@ -28,6 +28,7 @@ Automatic Typing}. * Abbrev Concepts:: Fundamentals of defined abbrevs. * Defining Abbrevs:: Defining an abbrev, so it will expand when typed. * Expanding Abbrevs:: Controlling expansion: prefixes, canceling expansion. +* Abbrevs Suggestions:: Get suggestions about defined abbrevs. * Editing Abbrevs:: Viewing or editing the entire list of defined abbrevs. * Saving Abbrevs:: Saving the entire list of abbrevs for another session. * Dynamic Abbrevs:: Abbreviations for words already in the buffer. @@ -223,6 +224,37 @@ changing this function you can make arbitrary changes to the abbrev expansion. @xref{Abbrev Expansion,,, elisp, The Emacs Lisp Reference Manual}. +@node Abbrev Suggestions +@section Abbrev Suggestions + + You can get abbrev suggestions when you manually type text for which +there is currently an active defined abbrev. For example, if there is +an abbrev @samp{foo} with the expansion @samp{find outer otter}, and +you manually type @samp{find outer otter}, the abbrev suggestion +feature will notice this and show a hint in the echo area when you +have stopped typing. + +@vindex abbrev-suggest + Enable the abbrev suggestion feature by setting +@code{abbrev-suggest} to @code{t}. + +@vindex abbrev-suggest-hint-threshold + Controls when to suggest an abbrev to the user. The variable +defines the number of characters that the user must save in order to +get a suggestion. For example, if the user types @samp{foo bar} +(seven characters) and there is an abbrev @samp{fubar} defined (five +characters), the user will not get any suggestion unless the threshold +is set to the number 2 or lower. With the default value 3, the user +would not get any suggestion, because the savings in using the abbrev +are not above the threshold. If you always want to get abbrev +suggestions, set this variable to 0. + +@findex abbrev-suggest-show-report + The command @code{abbrev-suggest-show-report} can be used to show a +buffer with all abbrev suggestions from the current editing session. +This can be useful if you get several abbrev suggestions and don't +remember them all. + @node Editing Abbrevs @section Examining and Editing Abbrevs diff --git a/etc/NEWS b/etc/NEWS index d3a97489d4d..9cd35593236 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1271,6 +1271,16 @@ This value customizes Emacs to use the style recommended in Damian Conway's book "Perl Best Practices" for indentation and formatting of conditionals. +** Abbrev mode + ++++ +*** Abbrev can now suggest pre-existing abbrevs based on typed text. +A new user option, 'abbrev-suggest', enables the new abbrev suggestion +feature. When enabled, if a user manually type a piece of text that +could have been written by using an abbrev, a hint will be displayed +in the echo area, mentioning the abbrev that could have been used +instead. + * New Modes and Packages in Emacs 28.1 diff --git a/lisp/abbrev.el b/lisp/abbrev.el index be6f9ee3437..75cc43941f4 100644 --- a/lisp/abbrev.el +++ b/lisp/abbrev.el @@ -824,6 +824,142 @@ see `define-abbrev' for details." "Function that `expand-abbrev' uses to perform abbrev expansion. Takes no argument and should return the abbrev symbol if expansion took place.") +(defcustom abbrev-suggest nil + "Non-nil means suggest abbrevs to the user. +By enabling this option, if abbrev mode is enabled and if the +user has typed some text that exists as an abbrev, suggest to the +user to use the abbrev by displaying a message in the echo area." + :type 'boolean + :version "28.1") + +(defcustom abbrev-suggest-hint-threshold 3 + "Threshold for when to inform the user that there is an abbrev. +The threshold is the number of characters that differ between the +length of the abbrev and the length of the expansion. The +thinking is that if the expansion is only one or a few characters +longer than the abbrev, the benefit of informing the user is not +that big. If you always want to be informed, set this value to +`0' or less. This setting only applies if `abbrev-suggest' is +non-nil." + :type 'number + :version "28.1") + +(defun abbrev--suggest-get-active-tables-including-parents () + "Return a list of all active abbrev tables, including parent tables." + (let* ((tables (abbrev--active-tables)) + (all tables)) + (dolist (table tables) + (setq all (append (abbrev-table-get table :parents) all))) + all)) + +(defun abbrev--suggest-get-active-abbrev-expansions () + "Return a list of all the active abbrev expansions. +Includes expansions from parent abbrev tables." + (let (expansions) + (dolist (table (abbrev--suggest-get-active-tables-including-parents)) + (mapatoms (lambda (e) + (let ((value (symbol-value (abbrev--symbol e table)))) + (when value + (push (cons value (symbol-name e)) expansions)))) + table)) + expansions)) + +(defun abbrev--suggest-count-words (expansion) + "Return the number of words in EXPANSION. +Expansion is a string of one or more words." + (length (split-string expansion " " t))) + +(defun abbrev--suggest-get-previous-words (n) + "Return the N words before point, spaces included." + (let ((end (point))) + (save-excursion + (backward-word n) + (replace-regexp-in-string + "\\s " " " + (buffer-substring-no-properties (point) end))))) + +(defun abbrev--suggest-above-threshold (expansion) + "Return non-nil if the abbrev in EXPANSION provides significant savings. +A significant saving, here, is the difference in length between +the abbrev and the abbrev expansion. EXPANSION is a cons cell +where the car is the expansion and the cdr is the abbrev." + (>= (- (length (car expansion)) + (length (cdr expansion))) + abbrev-suggest-hint-threshold)) + +(defvar abbrev--suggest-saved-recommendations nil + "Keeps a list of expansions that have abbrevs defined. +The user can show this list by calling +`abbrev-suggest-show-report'.") + +(defun abbrev--suggest-inform-user (expansion) + "Display a message to the user about the existing abbrev. +EXPANSION is a cons cell where the `car' is the expansion and the +`cdr' is the abbrev." + (run-with-idle-timer + 1 nil + (lambda () + (message "You can write `%s' using the abbrev `%s'." + (car expansion) (cdr expansion)))) + (push expansion abbrev--suggest-saved-recommendations)) + +(defun abbrev--suggest-shortest-abbrev (new current) + "Return the shortest abbrev of NEW and CURRENT. +NEW and CURRENT are cons cells where the `car' is the expansion +and the `cdr' is the abbrev." + (if (not current) + new + (if (< (length (cdr new)) + (length (cdr current))) + new + current))) + +(defun abbrev--suggest-maybe-suggest () + "Suggest an abbrev to the user based on the word(s) before point. +Uses `abbrev-suggest-hint-threshold' to find out if the user should be +informed about the existing abbrev." + (let (words abbrev-found word-count) + (dolist (expansion (abbrev--suggest-get-active-abbrev-expansions)) + (setq word-count (abbrev--suggest-count-words (car expansion)) + words (abbrev--suggest-get-previous-words word-count)) + (let ((case-fold-search t)) + (when (and (> word-count 0) + (string-match (car expansion) words) + (abbrev--suggest-above-threshold expansion)) + (setq abbrev-found (abbrev--suggest-shortest-abbrev + expansion abbrev-found))))) + (when abbrev-found + (abbrev--suggest-inform-user abbrev-found)))) + +(defun abbrev--suggest-get-totals () + "Return a list of all expansions and how many times they were used. +Each expansion is a cons cell where the `car' is the expansion +and the `cdr' is the number of times the expansion has been +typed." + (let (total cell) + (dolist (expansion abbrev--suggest-saved-recommendations) + (if (not (assoc (car expansion) total)) + (push (cons (car expansion) 1) total) + (setq cell (assoc (car expansion) total)) + (setcdr cell (1+ (cdr cell))))) + total)) + +(defun abbrev-suggest-show-report () + "Show the user a report of abbrevs he could have used." + (interactive) + (let ((totals (abbrev--suggest-get-totals)) + (buf (get-buffer-create "*abbrev-suggest*"))) + (set-buffer buf) + (erase-buffer) + (insert "** Abbrev expansion usage ** + +Below is a list of expansions for which abbrevs are defined, and +the number of times the expansion was typed manually. To display +and edit all abbrevs, type `M-x edit-abbrevs RET'\n\n") + (dolist (expansion totals) + (insert (format " %s: %d\n" (car expansion) (cdr expansion)))) + (display-buffer buf))) + (defun expand-abbrev () "Expand the abbrev before point, if there is an abbrev there. Effective when explicitly called even when `abbrev-mode' is nil. @@ -831,7 +967,9 @@ Calls the value of `abbrev-expand-function' with no argument to do the work, and returns whatever it does. (That return value should be the abbrev symbol if expansion occurred, else nil.)" (interactive) - (funcall abbrev-expand-function)) + (or (funcall abbrev-expand-function) + (if abbrev-suggest + (abbrev--suggest-maybe-suggest)))) (defun abbrev--default-expand () "Default function to use for `abbrev-expand-function'.