From: Mathias Dahl <mathias.dahl@gmail.com>
To: emacs-devel@gnu.org
Subject: [PATCH] Add abbrev suggestions
Date: Mon, 6 Jul 2020 01:40:40 +0200 [thread overview]
Message-ID: <CABrcCQ6GYzqzxBoeCfpXFEVoNDK6zy8FW4ZXe0Xdgp_6dHocVA@mail.gmail.com> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 1008 bytes --]
Hi,
After some time I decided to try to send a patch for this.
As for what it is, check the source :)
Or read this short documentation string for the new abbrev-suggest option:
* Non-nil means we should 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 instead."*
You can also read this thread to get the background:
https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00449.html
The recent discussions can be found here:
https://lists.gnu.org/archive/html/emacs-devel/2020-05/msg01605.html
If this patch looks good (a lot of changes has been done as per the
discussions with Eli, Stefan and others in the threads above, and I hope I
also managed to use the correct format for the patch), I'd be glad if
someone could commit this, and I will get started on the changes to the
Emacs documentation as well. I have signed papers before.
Thanks!
/Mathias
[-- Attachment #1.2: Type: text/html, Size: 1627 bytes --]
[-- Attachment #2: 0001-Add-abbrev-suggestions.patch --]
[-- Type: application/octet-stream, Size: 6610 bytes --]
From 32161858a4b34ec1f36fe0225dcd0b84ca8d43bc Mon Sep 17 00:00:00 2001
From: Mathias Dahl <mathias.dahl@gmail.com>
Date: Sun, 5 Jul 2020 18:44:57 -0400
Subject: [PATCH] Add abbrev suggestions
---
lisp/abbrev.el | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 139 insertions(+), 2 deletions(-)
diff --git a/lisp/abbrev.el b/lisp/abbrev.el
index 2d61a96..9690dac 100644
--- a/lisp/abbrev.el
+++ b/lisp/abbrev.el
@@ -830,9 +830,144 @@ abbrev-expand-functions
(make-obsolete-variable 'abbrev-expand-functions 'abbrev-expand-function "24.4")
(defvar abbrev-expand-function #'abbrev--default-expand
- "Function that `expand-abbrev' uses to perform abbrev expansion.
+ "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 t
+ "Non-nil means we should 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 instead."
+ :type 'boolean
+ :group 'abbrev-mode)
+
+(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 differs 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."
+ :type 'number
+ :group 'abbrev-mode)
+
+(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 previous N words, spaces included.
+Changes newlines into spaces."
+ (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 t if we are above the threshold.
+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.
+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 their usage.
+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.
@@ -842,7 +977,9 @@ expand-abbrev
be the abbrev symbol if expansion occurred, else nil.)"
(interactive)
(run-hooks 'pre-abbrev-expand-hook)
- (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'.
--
1.9.1
next reply other threads:[~2020-07-05 23:40 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-05 23:40 Mathias Dahl [this message]
2020-07-19 17:40 ` [PATCH] Add abbrev suggestions Mathias Dahl
2020-07-19 19:01 ` Eli Zaretskii
2020-07-25 8:12 ` Eli Zaretskii
2020-08-11 22:16 ` Mathias Dahl
2020-08-13 13:59 ` Eli Zaretskii
2020-08-13 14:29 ` Mathias Dahl
2020-09-14 22:04 ` Mathias Dahl
2020-09-15 6:20 ` Andreas Röhler
2020-09-18 8:39 ` Mathias Dahl
2020-09-15 8:16 ` Robert Pluim
2020-09-18 8:40 ` Mathias Dahl
2020-09-24 20:02 ` Mathias Dahl
2020-09-25 8:09 ` Robert Pluim
2020-09-25 20:42 ` Mathias Dahl
2020-09-26 14:19 ` Robert Pluim
2020-09-26 20:56 ` Mathias Dahl
2020-09-26 22:21 ` Stefan Monnier
2020-09-27 6:12 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CABrcCQ6GYzqzxBoeCfpXFEVoNDK6zy8FW4ZXe0Xdgp_6dHocVA@mail.gmail.com \
--to=mathias.dahl@gmail.com \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).