From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Mathias Dahl Newsgroups: gmane.emacs.devel Subject: Re: Abbrev suggestions - feedback appreciated Date: Sat, 7 Oct 2017 20:40:10 +0200 Message-ID: References: NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="001a114fdde2c1a435055af94a47" X-Trace: blaine.gmane.org 1507401649 5037 195.159.176.226 (7 Oct 2017 18:40:49 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 7 Oct 2017 18:40:49 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Oct 07 20:40:40 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1e0u1b-0008Cr-4a for ged-emacs-devel@m.gmane.org; Sat, 07 Oct 2017 20:40:35 +0200 Original-Received: from localhost ([::1]:50860 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e0u1i-0000u8-Bl for ged-emacs-devel@m.gmane.org; Sat, 07 Oct 2017 14:40:42 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:35238) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e0u1a-0000u2-CV for emacs-devel@gnu.org; Sat, 07 Oct 2017 14:40:36 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1e0u1Y-0002dk-DT for emacs-devel@gnu.org; Sat, 07 Oct 2017 14:40:34 -0400 Original-Received: from mail-qk0-x22a.google.com ([2607:f8b0:400d:c09::22a]:50076) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1e0u1Y-0002dQ-4b for emacs-devel@gnu.org; Sat, 07 Oct 2017 14:40:32 -0400 Original-Received: by mail-qk0-x22a.google.com with SMTP id s14so7350637qks.6 for ; Sat, 07 Oct 2017 11:40:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=dONIoRgqME+A1ryArKsusWILXc8KeUTJvjt2iepYlZg=; b=noe2jcz5UkghT1HmclIxL6ByU9qrRpGUhqQYbxtmsplINH/5M2iMWMiQHM8o+wkPSh lc6e8ILco5Q/E9Q8CckMP8rYI/Vns8NqFZtXbx1zEmF6k9HOPZt/A/R6oxqdTXCI8Pbe 90UBhvL6vHbqEXWC/NvjZq1VvLBHebI6LzRXS7qrOteL9OHM6F7lSgwTXBn78MriTCSA GWHdtHy2+ne5UqPUv1PaOUk9+iuVNnEd3aKB/95RvHRUhbrNbQIq7GtuAtULdaOehtDD yn3adDb13mYD1hmk8vSmc21X/CMwnS47yYD38icPG24+SVVEqtOd4V3anwqg/VTxMuPa Cg4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=dONIoRgqME+A1ryArKsusWILXc8KeUTJvjt2iepYlZg=; b=bNs6Sma5O4b8FggixGWmn6a6iZ4pCohxM3huF9JusHJkGfigzHlMnrRlqSVuXqgvhO 92YcS+kyemebOwK7LuRR00Ecq4STEye4QrSYKRq5sMqcndewAmQWTW/KVxEnbhptvnO4 apLKt6np/oU4+q1YAis/mqZ2g7krgFVS3aoKcPz382UyNdDgw3Wxls+elLi7CUdK7vAu QE11L9yV0qQTV/9GVpEwJizb0XfXZqDTrMw24phaw9WTkGJ4Sr3cdC3imRhyaej1EACV H4Zf6TNVAn3gMXcVsCbcGvC+SLYwyo3GarnXd3kIdSMwh6sXtPVnDhN4adVGDTRkahqb tyBA== X-Gm-Message-State: AMCzsaWP32ZNL8USX+/feqp9wSEBvC4q1AfzPWDdW+O3LXTm7hJxiO3x ow71rWBT3j1zxQeThGu6jhJg6cMM4I+Vy2GRXvrEgZnv X-Google-Smtp-Source: AOwi7QDbE0dsoSAXDS867GVWZFcYR8eIw9MOA3WEMD4gVglJhRkY6OoY7X/J9RSwxru4p3Jhp7NVVOOWk7/bhJWpg3g= X-Received: by 10.55.105.130 with SMTP id e124mr2182628qkc.314.1507401631113; Sat, 07 Oct 2017 11:40:31 -0700 (PDT) Original-Received: by 10.140.85.170 with HTTP; Sat, 7 Oct 2017 11:40:10 -0700 (PDT) In-Reply-To: X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400d:c09::22a X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:219231 Archived-At: --001a114fdde2c1a435055af94a47 Content-Type: text/plain; charset="UTF-8" Hi again, Please find below the latest version. It has the option requested earlier and I also included the command `absug-report' that the user can use to see what expansions that has been typed manually, and how many times. I'm starting to be quite happy with this now. This is still a separate package, but if there is still interest I can try to merge the code into abbrev.el (if so, I have some renaming to do...) and make it into an option there, as suggested by Eli earlier. Then I can post a patch here. Comments? /Mathias ;;; absug.el --- Suggest an abbrev based on the word(s) before point ;; Copyright stuff to be added later... ;; Author: Mathias Dahl (mathias.dahl@gmail.com) ;;; Commentary: ;; This library helps the user remember defined abbrevs by suggesting ;; them after having typed an abbrev expansion manually. ;; For example, if the user has defined the abbrev `doc' with the ;; expansion `document', if the user manually types `document' with ;; `abbrev-mode' active, a message will be presented suggesting to use ;; the defined abbrev `doc' instead. ;; To install, load or require this file/library and also add the ;; following code to your .emacs or init.el file: ;; (absug-enable) ;; (It is also a command you can call interactively) ;; You can interactively turn off abbrev suggestions by calling the ;; command `absug-disable'. ;;; Ideas: ;; - Think about different ways to notify the user ;; - Display a report of statistics of abbrevs the user have forgotten ;;; Code: (defcustom absug-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 :group 'convenience) (defun absug-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 absug-get-active-abbrev-expansions () "Return a list of all the active abbrev expansions. Includes expansions from parent abbrev tables." (let (expansions) (dolist (table (absug-get-active-tables-including-parents)) (mapatoms (lambda (e) (let ((value (symbol-value (abbrev--symbol e table)))) (when value (setq expansions (cons (cons value (symbol-name e)) expansions))))) table)) expansions)) (defun absug-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 absug-get-previous-words (n) "Return the previous N words, spaces included. Changes newlines into spaces." (let ((end (point)) words) (save-excursion (backward-word n) (replace-regexp-in-string "\\s " " " (buffer-substring-no-properties (point) end))))) (defun absug-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))) absug-hint-threshold)) (defvar absug-saved-recommendations nil "Keeps a list of expansions that have abbrevs defined. The user can show this list by calling `absug-show-recommendations'.") (defun absug-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 2 nil `(lambda () (message "You can write `%s' using the abbrev `%s'." ,(car expansion) ,(cdr expansion)))) (setq absug-saved-recommendations (cons expansion absug-saved-recommendations))) (defun absug-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 absug-maybe-suggest () "Suggest an abbrev to the user based on the word(s) before point. Uses `absug-hint-threshold' to find out if the user should be informed about the existing abbrev." (let (words abbrev-found expansion word-count) (dolist (expansion (absug-get-active-abbrev-expansions)) (setq word-count (absug-count-words (car expansion)) words (absug-get-previous-words word-count)) (let ((case-fold t)) (when (and (> word-count 0) (string-match (car expansion) words) (absug-above-threshold expansion)) (setq abbrev-found (absug-shortest-abbrev expansion abbrev-found))))) (when abbrev-found (absug-inform-user abbrev-found)))) (defun absug-default-expand () "My version to use for `abbrev-expand-function'. If no abbrev expansion is found by `abbrev--default-expand', see if there is an abbrev defined for the word before point, and suggest it to the user." (unless (abbrev--default-expand) (absug-maybe-suggest))) (defun absug-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 absug-saved-recommendations) (if (not (assoc (car expansion) total)) (add-to-list 'total (cons (car expansion) 1)) (setq cell (assoc (car expansion) total)) (setcdr cell (1+ (cdr cell))))) total)) (defun absug-report () "Show the user a report of abbrevs he could have used." (interactive) (let ((totals (absug-get-totals)) (buf (get-buffer-create "*absug*"))) (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 absug-disable () "Disable abbrev suggestions." (interactive) (setq abbrev-expand-function #'abbrev--default-expand)) (defun absug-enable () "Enable abbrev suggestions." (interactive) (setq abbrev-expand-function #'absug-default-expand)) (provide 'absug) ;;; absug.el ends here --001a114fdde2c1a435055af94a47 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi again,

=
Please find below the latest version. It has the= option requested
earlier and I also includ= ed the command `absug-report' that the user can
use to see what expansions that has been typed manually, and how m= any
times.
=
I'm starting to be quite happy wit= h this now. This is still a separate
packag= e, but if there is still interest I can try to merge the code into
abbrev.el (if so, I have some renaming to do...) an= d make it into an
option there, as suggeste= d by Eli earlier. Then I can post a patch here.

Comments?

/Mathias

=
;;; absug= .el --- Suggest an abbrev based on the word(s) before point

;; Copyright stuff to= be added later...

;; Author: Mathias Dahl (mathias.dahl@gmail.com)

=
;;; Commentary:
=
;; This library helps the user remembe= r defined abbrevs by suggesting
;; them aft= er having typed an abbrev expansion manually.

;; For example, if the user has def= ined the abbrev `doc' with the
;; expan= sion `document', if the user manually types `document' with
;; `abbrev-mode' active, a message will be pre= sented suggesting to use
;; the defined abb= rev `doc' instead.

;; To install, load or require this file/library and also = add the
;; following code to your .emacs or= init.el file:

;; (absug-enable)

;; (It is also a command you can call interactively)

;; You = can interactively turn off abbrev suggestions by calling the
;; command `absug-disable'.

;;; Ideas:

;; - Think about different= ways to notify the user
;; - Display a rep= ort of statistics of abbrevs the user have forgotten

;;; Code:

(defcustom absug-hint-th= reshold 3
=C2=A0 "Threshold for when t= o 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 expansio= n.=C2=A0 The
thinking is that if the expans= ion is only one or a few characters
longer = than the abbrev, the benefit of informing the user is not
that big.=C2=A0 If you always want to be informed, set thi= s value to
`0' or less."
=C2=A0 :type 'number
=C2=A0 :group 'abbrev-mode
=C2=A0 = :group 'convenience)

(defun absug-get-active-tables-including-parents ()
=C2=A0 "Return a list of all active abbrev= tables, including parent tables."
=C2= =A0 (let* ((tables (abbrev--active-tables))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(all tables))
=C2=A0 =C2=A0 (dolist (table tables)
= =C2=A0 =C2=A0 =C2=A0 (setq all (append (abbrev-table-get table :parents) al= l)))
=C2=A0 =C2=A0 all))

(defun absug-get-active-= abbrev-expansions ()
=C2=A0 "Return a = list of all the active abbrev expansions.
I= ncludes expansions from parent abbrev tables."
=C2=A0 (let (expansions)
=C2=A0 = =C2=A0 (dolist (table (absug-get-active-tables-including-parents))
=C2=A0 =C2=A0 =C2=A0 (mapatoms (lambda (e)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 (let ((value (symbol-value (abbrev--symbol e table))))<= div class=3D"gmail_extra">=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 (when value
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (setq ex= pansions
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cons (cons= value (symbol-name e))
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 expansions)))))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 table))
=C2=A0 =C2=A0 expansions))

(defun absug-count-words (expans= ion)
=C2=A0 "Return the number of word= s in EXPANSION.
Expansion is a string of on= e or more words."
=C2=A0 (length (spli= t-string expansion " " t)))

<= /div>
(defun absug-get-previous-words (n)
=C2=A0 "Return the previous N words, spaces i= ncluded.
Changes newlines into spaces."= ;
=C2=A0 (let ((end (point))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 words)
=C2=A0 =C2=A0 (save-excursion
=C2= =A0 =C2=A0 =C2=A0 (backward-word n)
=C2=A0 = =C2=A0 =C2=A0 (replace-regexp-in-string
=C2= =A0 =C2=A0 =C2=A0 =C2=A0"\\s " " "
=C2=A0 =C2=A0 =C2=A0 =C2=A0(buffer-substring-no-properties (poi= nt) end)))))

(defun absug-above-threshold (expansion)
=C2=A0 "Return t if we are above the threshold.
EXPANSION is a cons cell where the car is the expansion an= d the
cdr is the abbrev."
=C2=A0 (>=3D (- (length (car expansion))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(length (cdr expansi= on)))
=C2=A0 =C2=A0 =C2=A0 absug-hint-thres= hold))

(defvar absug-saved-recommendations nil
= =C2=A0 "Keeps a list of expansions that have abbrevs defined.
The user can show this list by calling
`absug-show-recommendations'.")

(defun absug-inform-= user (expansion)
=C2=A0 "Display a mes= sage to the user about the existing abbrev.
EXPANSION is a cons cell where the `car' is the expansion and the
`cdr' is the abbrev."
=C2=A0 (run-with-idle-timer
=C2=A0 =C2=A02 nil
=C2=A0 =C2=A0`(lambda = ()
=C2=A0 =C2=A0 =C2=A0 (message "You = can write `%s' using the abbrev `%s'."
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0,(car expan= sion) ,(cdr expansion))))
=C2=A0 (setq absu= g-saved-recommendations
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 (cons expansion absug-saved-recommendations)))

(defun absug-shortest= -abbrev (new current)
=C2=A0 "Return t= he shortest abbrev.
NEW and CURRENT are con= s cells where the `car' is the expansion
and the `cdr' is the abbrev."
= =C2=A0 (if (not current)
=C2=A0 =C2=A0 =C2= =A0 new
=C2=A0 =C2=A0 (if (< (length (cd= r new))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0(length (cdr current)))
=C2=A0 =C2=A0= =C2=A0 =C2=A0 new
=C2=A0 =C2=A0 =C2=A0 cur= rent)))

(defun absug-maybe-suggest ()
=C2=A0 &quo= t;Suggest an abbrev to the user based on the word(s) before point.
Uses `absug-hint-threshold' to find out if the = user should be
informed about the existing = abbrev."
=C2=A0 (let (words abbrev-fou= nd expansion word-count)
=C2=A0 =C2=A0 (dol= ist (expansion (absug-get-active-abbrev-expansions))
=C2=A0 =C2=A0 =C2=A0 (setq word-count (absug-count-words (car exp= ansion))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 words (absug-get-previous-words word-count))
=C2=A0 =C2=A0 =C2=A0 (let ((case-fold t))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (when (and (> word-count 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0(string-match (car expansion) words)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(absug-above-threshold expansion))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (setq abbrev-found (absug-shortest-abb= rev
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expansio= n abbrev-found)))))
=C2=A0 =C2=A0 (when abb= rev-found
=C2=A0 =C2=A0 =C2=A0 (absug-infor= m-user abbrev-found))))

(defun absug-default-expand ()
=C2=A0 "My version to use for `abbrev-expand-function'.
If no abbrev expansion is found by `abbrev--def= ault-expand', see
if there is an abbrev= defined for the word before point, and
sug= gest it to the user."
=C2=A0 (unless (= abbrev--default-expand)
=C2=A0 =C2=A0 (absu= g-maybe-suggest)))

(defun absug-get-totals ()
=C2= =A0 "Return a list of all expansions and their usage.
Each expansion is a cons cell where the `car' is the e= xpansion
and the `cdr' is the number of= times the expansion has been
typed."<= /div>
=C2=A0 (let (total cell)
=C2=A0 =C2=A0 (dolist (expansion absug-saved-recommendations)=
=C2=A0 =C2=A0 =C2=A0 (if (not (assoc (car = expansion) total))
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (add-to-list 'total (cons (car expansion) 1))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (setq cell (assoc (car expans= ion) total))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (s= etcdr cell (1+ (cdr cell)))))
=C2=A0 =C2=A0= total))

(defun absug-report ()
=C2=A0 "Show= the user a report of abbrevs he could have used."
=C2=A0 (interactive)
=C2=A0 (l= et ((totals (absug-get-totals))
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 (buf (get-buffer-create "*absug*")))
=C2=A0 =C2=A0 (set-buffer buf)
=C2=A0 =C2=A0 (erase-buffer)
=C2= =A0 =C2=A0 (insert "** Abbrev expansion usage **

Below is a list of expansio= ns for which abbrevs are defined, and
the n= umber of times the expansion was typed manually. To display
and edit all abbrevs, type `M-x edit-abbrevs RET'\n\n&= quot;)
=C2=A0 =C2=A0 (dolist (expansion tot= als)
=C2=A0 =C2=A0 =C2=A0 (insert (format &= quot; %s: %d\n" (car expansion) (cdr expansion))))
=C2=A0 =C2=A0 (display-buffer buf)))

(defun absug-disable ()
=C2=A0 "Disable abbrev suggestions."
=C2=A0 (interactive)
=C2=A0 (setq abbrev-expand-function #'abbrev--default-expand))

(defun = absug-enable ()
=C2=A0 "Enable abbrev = suggestions."
=C2=A0 (interactive)
=C2=A0 (setq abbrev-expand-function #'absu= g-default-expand))

(provide 'absug)

;;; absug.el ends here

--001a114fdde2c1a435055af94a47--