emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Abbrev suggestions - feedback appreciated


From: Mathias Dahl
Subject: Re: Abbrev suggestions - feedback appreciated
Date: Sat, 7 Oct 2017 20:40:10 +0200

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 (address@hidden)

;;; 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


reply via email to

[Prev in Thread] Current Thread [Next in Thread]