PredictiveAbbreviation

Description

This is another abbreviation expansion in the line of DynamicAbbreviations. The difference is that this minor mode will analyse text during idle time. Thus, the abbreviations are always displayed!

pabbrev’s main entry point is through the minor mode ‘pabbrev-mode’. There is also a global minor mode, called ‘global-pabbrev-mode’, which does the same in all appropriate buffers.

Download

Lisp:pabbrev.el

This version is now updated for Emacs 24.

Customizations

Normally hitting TAB a second time after a completion will take you to a suggestions buffer where you can select from alternate completions. This bit of elisp hackery will instead use a pop-up list via auto-competion mode. Make sure this is executed after pabbrev is loaded.

(require 'auto-complete)

(defun pabbrevx-ac-on-pre-command ()
  (if (or (eq this-command 'self-insert-command)
          (and (not (ac-trigger-command-p))
               (or (not (symbolp this-command))
                   (not (string-match "^ac-" (symbol-name this-command))))))
      (progn
        (remove-hook 'post-command-hook 'pabbrevx-ac-on-post-command t)
        (remove-hook 'pre-command-hook 'pabbrevx-ac-on-pre-command t)
        (ac-abort))))

(defun pabbrevx-ac-on-post-command ()
  (if (and (not isearch-mode)
           (ac-trigger-command-p))
      (pabbrevx-ac-start)))

(defun pabbrevx-ac-start ()
  (let ((candidates (mapcar 'car pabbrev-expansion-suggestions)))
    (add-hook 'pre-command-hook 'pabbrevx-ac-on-pre-command nil t)
    (add-hook 'post-command-hook 'pabbrevx-ac-on-post-command nil t)
    ;; ripped straight from ac-start
    (let* ((point (save-excursion (funcall ac-prefix-function)))
           (reposition (or (not (equal ac-point point))
                           ;; If menu direction is positive and next visual line belongs
                           ;; to same buffer line, then need reposition
                           (and (> ac-menu-direction 0)
                                (ac-menu-at-wrapped-line)))))
      (if (null point)
          (progn
            ;; need to nil this so pabbrev-expand-maybe-full we won't try
            ;; pabbrev expansion if user hits another TAB after ac aborts
            (setq pabbrev-last-expansion-suggestions nil)
            (ac-abort))          
        (setq ac-buffer (current-buffer))
        (setq ac-point point)
        (when (not (equal ac-point ac-old-point))
          (setq ac-old-point point))
        (setq ac-prefix (buffer-substring-no-properties point (point)))
        (setq ac-limit ac-candidate-limit)
        (if (or reposition (null ac-menu))
            (save-excursion
              (funcall ac-init-function)))
        (let (;;candidates
              width
              (current-width (if ac-menu (ac-menu-width ac-menu) 0)))
          ;; (setq candidates (if (or ac-completing
          ;;                          (not (integerp ac-auto-start))
          ;;                          (>= (length ac-prefix) ac-auto-start))
          ;;                      (save-excursion
          ;;                        (funcall ac-candidate-function))))
          ;;(if ac-candidate-filter-function
          ;;    (setq candidates (funcall ac-candidate-filter-function candidates)))
          (setq width (let ((w '(0)) s)
                        (dotimes (i ac-candidate-menu-height)
                          (setq s (nth i candidates))
                          (if (stringp s) (push (string-width s) w)))
                        (apply 'max w)))
          (if (or reposition
                  (null ac-menu)
                  (> width current-width)
                  (< width (- current-width 10)))
              (ac-setup (* (ceiling (/ width 10.0)) 10)))
          (ac-update-candidates candidates))))))

(defun pabbrevx-suggestions-goto-buffer (suggestions)
  (pabbrevx-ac-start))

(fset 'pabbrev-suggestions-goto-buffer 'pabbrevx-suggestions-goto-buffer)

This hack does not work with the latest stable auto-complete version because ac-prefix-function does not exist. Could somebody update it, please?

The latest version of auto-complete has now split the menu code into a dedicated package, which makes this much simpler. Again, make sure this is executed after pabbrev is loaded

(require 'popup)

(defun pabbrevx-suggestions-goto-buffer (suggestions)
  (let* ((candidates (mapcar 'car suggestions))
         (bounds (pabbrev-bounds-of-thing-at-point))
         (selection (popup-menu* candidates
                                 :point (car bounds)
                                 :scroll-bar t)))
    (when selection
      ;; modified version of pabbrev-suggestions-insert
      (let ((point))
        (save-excursion
          (progn
            (delete-region (car bounds) (cdr bounds))
            (insert selection)
            (setq point (point))))
        (if point
            (goto-char point))
        ;; need to nil this so pabbrev-expand-maybe-full won't try
        ;; pabbrev expansion if user hits another TAB after ac aborts
        (setq pabbrev-last-expansion-suggestions nil)
        ))))
      
(fset 'pabbrev-suggestions-goto-buffer 'pabbrevx-suggestions-goto-buffer)

Here’s an alternative to the above: hitting TAB a second time uses ido-mode to display all suggestions.

(defun pabbrev-suggestions-ido (suggestion-list)
  "Use ido to display menu of all pabbrev suggestions."
  (when suggestion-list
    (pabbrev-suggestions-insert-word pabbrev-expand-previous-word)
    (pabbrev-suggestions-insert-word
     (ido-completing-read "Completions: " (mapcar 'car suggestion-list)))))

(defun pabbrev-suggestions-insert-word (word)
  "Insert word in place of current suggestion, with no attempt to kill pabbrev-buffer."
  (let ((point))
    (save-excursion
      (let ((bounds (pabbrev-bounds-of-thing-at-point)))
	(progn
	  (delete-region (car bounds) (cdr bounds))
	  (insert word)
	  (setq point (point)))))
    (if point
	(goto-char point))))

(fset 'pabbrev-suggestions-goto-buffer 'pabbrev-suggestions-ido)

CategoryCompletion