Download
(require 'cl)
(defconst *completion-version* "11.2"
"Tested for EMACS versions 18.49, 18.52, 18.55, 19.19, 19.30 and 19.34, and
Epoch 3.2 and Epoch 4.0.")
(defconst cmpl-emacs-major-version
(string-to-number (substring emacs-version 0
(string-match "\\." emacs-version))))
(defconst cmpl-emacs-minor-version
(let ((start (1+ (string-match "\\." emacs-version))))
(string-to-number (substring emacs-version start
(string-match "\\." emacs-version start)))))
(defvar *completep* t
"*Set to nil to turn off the completion hooks.
(No new words added to the database or saved to the init file)."
)
(defvar *save-completions-p* t
"*If non-nil, the most useful completions are saved to disk when
exiting EMACS. See *saved-completions-decay-factor*.")
(defvar *saved-completions-filename* "~/.completions"
"*The filename to save completions to.")
(defvar *saved-completion-retention-time* 336
"*The maximum amout of time to save a completion for if it has not been used.
In hours. (1 day = 24, 1 week = 168). If this is 0, non-permanent completions
will not be saved unless these are used. Default is two weeks."
)
(defvar *separator-character-uses-completion-p* nil
"*If non-nil, typing a separator character after a completion symbol that
is not part of the database marks it as used (so it will be saved).")
(defvar *completion-file-versions-kept* kept-new-versions
"*Set this to the number of versions you want save-completions-to-file
to keep.")
(defvar *print-next-completion-speed-threshold* 4800
"*The baud rate at or above which to print the next potential completion
after inserting the current one."
)
(defvar *print-next-completion-does-cdabbrev-search-p* nil
"*If non-NIL, the next completion prompt will also do a cdabbrev search.
This can be time consuming.")
(defvar *cdabbrev-radius* 15000
"*How far to search for cdabbrevs. In number of characters. If nil, the
whole buffer is searched.")
(defvar *modes-for-completion-find-file-hook* '(lisp c)
"*A list of modes {either c or lisp}. Definitions from visited files
of those types are automatically added to the completion database.")
(defvar *completion-find-file-hook-lisp-major-modes* '(emacs-lisp-mode lisp-mode)
"*A list of major mode symbols for which cmpl-find-file-hook and
add-completions-from-file should use the lisp parser.")
(defvar *completion-find-file-hook-c-major-modes* '(c-mode c++-mode)
"*A list of major mode symbols for which cmpl-find-file-hook and
add-completions-from-file should use the c parser.")
(defvar *record-cmpl-statistics-p* nil
"*If non-nil, statistics are automatically recorded.")
(defvar *completion-auto-save-period* 1800
"*The period in seconds to wait for emacs to be idle before autosaving
the completions. Default is a 1/2 hour.")
(defconst *completion-min-length* nil
"*The minimum length of a stored completion.
THIS CANNOT BE CHANGED IN YOUR .emacs ! It is used at compile time.
To change:
1) edit the function completion-eval-when and save your changes
2) (load-file \"completion.el\")
3) (byte-compile-file \"completion.el\")
")
(defconst *completion-max-length* nil
"*The maximum length of a stored completion.
THIS CANNOT BE CHANGED IN YOUR .emacs ! It is used at compile time.
To change:
1) edit the function completion-eval-when and save your changes
2) (load-file \"completion.el\")
3) (byte-compile-file \"completion.el\")
")
(defconst *completion-prefix-min-length* nil
"The minimum length of a completion search string.
THIS CANNOT BE CHANGED IN YOUR .emacs ! It is used at compile time.
To change:
1) edit the function completion-eval-when and save your changes
2) (load-file \"completion.el\")
3) (byte-compile-file \"completion.el\")
")
(defmacro eval-when-compile-load-eval (&rest body)
(mapcar 'eval body)
(cons 'progn body)
)
(defun completion-eval-when ()
(eval-when-compile-load-eval
(setq *completion-min-length* 6)
(setq *completion-max-length* 200)
(setq *completion-prefix-min-length* 3)
(require 'cl)
))
(completion-eval-when)
(defvar cmpl-initialized-p nil
"Set to t when the completion system is initialized. Indicates that the old
completion file has been read in.")
(defvar cmpl-completions-accepted-p nil
"Set to T as soon as the first completion has been accepted. Used to
decide whether to save completions.")
(defun remove (item list)
(setq list (copy-sequence list))
(delq item list)
)
(defun minibuffer-window-selected-p ()
"True iff the current window is the minibuffer."
(eq (minibuffer-window) (selected-window)))
(eval-when-compile-load-eval
(defun function-needs-autoloading-p (symbol)
(and (fboundp symbol)
(listp (symbol-function symbol))
(eq 'autoload (car (symbol-function symbol)))
))
)
(defun function-defined-and-loaded (symbol)
(and (fboundp symbol) (not (function-needs-autoloading-p symbol)))
)
(defmacro read-time-eval (form)
(eval form)
)
(defmacro cmpl-scan-sexps (from count)
`(condition-case nil (scan-sexps (min (point-max) ,from) ,count) (error (point))))
(defun cmpl-baud-rate ()
(if (>= cmpl-emacs-major-version 19)
(symbol-value 'baud-rate)
(let ((fcn 'baud-rate))
(funcall fcn)
)))
(defmacro completion-advise (function-name where &rest body)
"Adds the body code before calling function. This advise is not compiled.
WHERE is either :BEFORE or :AFTER."
(completion-advise-1 function-name where body)
)
(defmacro cmpl-apply-as-top-level (function arglist)
"Calls function-name interactively if inside a call-interactively."
(list 'cmpl-apply-as-top-level-1 function arglist
'(let ((old-em executing-kbd-macro)
retval)
(setq executing-kbd-macro nil)
(setq retval (interactive-p))
(setq executing-kbd-macro old-em)
retval))
)
(defun cmpl-apply-as-top-level-1 (function arglist interactive-p)
(if (and interactive-p (commandp function))
(call-interactively function)
(apply function arglist)
))
(eval-when-compile-load-eval
(defun cmpl-defun-preamble (function-name)
(let ((doc-string
(condition-case e
(documentation function-name)
(error nil)))
(interactivep (commandp function-name))
)
(append
(if doc-string (list doc-string))
(if interactivep '((interactive)))
)))
(defun completion-advise-1 (function-name where body &optional new-name)
(unless new-name (setq new-name function-name))
(let ((quoted-name (list 'quote function-name))
(quoted-new-name (list 'quote new-name))
(replace-name
(intern (concat "$$$cmpl-" (symbol-name function-name))))
)
(list 'progn
(list 'defvar replace-name
(list 'symbol-function quoted-name))
(list*
'defun new-name '(&rest arglist)
(append
(cmpl-defun-preamble function-name)
(list (list 'cond
(list (list 'and (list 'listp replace-name)
(list 'eq ''autoload
(list 'car replace-name)))
(list 'completion-advise-do-autoload
quoted-name
(list 'quote replace-name)
(list 'quote where)
(list 'quote body)
quoted-new-name)
(list 'cmpl-apply-as-top-level
quoted-new-name 'arglist)
)
(list* 't
(ecase where
(:before
(list (cons 'progn body)
(list 'cmpl-apply-as-top-level
replace-name 'arglist)))
(:after
(list* (list 'cmpl-apply-as-top-level
replace-name 'arglist)
body)
)))
)))
))
))
)
(defun completion-advise-do-autoload
(function-name replace-name where body new-name)
(interactive)
(let ((filename (second (symbol-value replace-name)))
)
(makunbound replace-name)
(load filename)
(cond ((not (function-defined-and-loaded function-name))
(error "The autoload file %s did not define the function %s"
filename function-name)
)
(t
(eval (completion-advise-1 function-name where body new-name))
))
))
(defun cmpl-string-case-type (string)
"Returns :capitalized, :up, :down, :mixed, or :neither."
(let ((case-fold-search nil))
(cond ((string-match "[a-z]" string)
(cond ((string-match "[A-Z]" string)
(cond ((and (> (length string) 1)
(null (string-match "[A-Z]" string 1)))
':capitalized)
(t
':mixed)))
(t ':down)))
(t
(cond ((string-match "[A-Z]" string)
':up)
(t ':neither))))
))
(defun cmpl-coerce-string-case (string case-type)
(cond ((eq case-type ':down) (downcase string))
((eq case-type ':up) (upcase string))
((eq case-type ':capitalized)
(setq string (downcase string))
(aset string 0 (logand ?\337 (aref string 0)))
string)
(t string)
))
(defun cmpl-merge-string-cases (string-to-coerce given-string)
(let ((string-case-type (cmpl-string-case-type string-to-coerce))
)
(cond ((memq string-case-type '(:down :up :capitalized))
(cmpl-coerce-string-case string-to-coerce
(cmpl-string-case-type given-string))
)
(t
string-to-coerce)
)))
(defvar cmpl-emacs-idle-interval 150
"Seconds between running the emacs idle process.")
(defun init-cmpl-emacs-idle-process ()
"Initialize the emacs idle process."
(run-at-time t cmpl-emacs-idle-interval 'cmpl-emacs-idle-handler)
)
(defvar cmpl-emacs-buffer nil)
(defvar cmpl-emacs-point 0)
(defvar cmpl-emacs-last-command nil)
(defvar cmpl-emacs-last-command-char nil)
(defun cmpl-emacs-idle-p ()
(if (and (eq cmpl-emacs-buffer (current-buffer))
(= cmpl-emacs-point (point))
(eq cmpl-emacs-last-command last-command)
(eq last-command-char last-command-char)
)
t
(setq cmpl-emacs-buffer (current-buffer))
(setq cmpl-emacs-point (point))
(setq cmpl-emacs-last-command last-command)
(setq last-command-char last-command-char)
nil
))
(defvar cmpl-emacs-idle-time 0
"The idle time of emacs in seconds.")
(defvar inside-cmpl-emacs-idle-handler nil)
(defvar cmpl-emacs-idle-time-hooks nil)
(defun cmpl-emacs-idle-handler (proc string)
(if (cmpl-emacs-idle-p)
(incf cmpl-emacs-idle-time cmpl-emacs-idle-interval)
(setq cmpl-emacs-idle-time 0))
(unless inside-cmpl-emacs-idle-handler
(setq inside-cmpl-emacs-idle-handler t)
(dolist (function cmpl-emacs-idle-time-hooks)
(condition-case e
(funcall function)
(error nil)
))
(setq inside-cmpl-emacs-idle-handler nil)
))
(defun hours-since-1970 (&optional time-list)
"""Hours since 1970, as a float."""
(let* ((sec-per-hour 3600.)
(time (or time-list (current-time)))
)
(/ (+ (* (float (car time)) 65536.) (car (cdr time)))
(float sec-per-hour))
))
(defun cmpl-hours-since-1900 (&optional time-list)
"""TIME-LIST is a list like that returned from (current-time),
hi 16 bits of sec-since-1970, low 16 bits, and microsec.
Returns hours since 1900, as an integer (truncated).
"""
(let ((hours-from-1900-to-1970 613606)
)
(truncate (+ (hours-since-1970 time-list) hours-from-1900-to-1970))
))
(defun make-standard-completion-syntax-table ()
(let ((table (copy-syntax-table))
)
(dotimes (i 256)
(modify-syntax-entry i " " table))
(dotimes (i 26)
(modify-syntax-entry (+ ?a i) "_" table)
(modify-syntax-entry (+ ?A i) "_" table))
(dotimes (i 10)
(modify-syntax-entry (+ ?0 i) "_" table))
(let ((symbol-chars '(?@ ?/ ?\\ ?* ?+ ?~ ?$ ?%))
(symbol-chars-ignore '(?_ ?- ?: ?.))
)
(dolist (char symbol-chars)
(modify-syntax-entry char "_" table))
(dolist (char symbol-chars-ignore)
(modify-syntax-entry char "w" table)
)
)
table))
(defconst cmpl-standard-syntax-table (make-standard-completion-syntax-table))
(defun make-lisp-completion-syntax-table ()
(let ((table (copy-syntax-table cmpl-standard-syntax-table))
(symbol-chars '(?! ?& ?? ?= ?^))
)
(dolist (char symbol-chars)
(modify-syntax-entry char "_" table))
table))
(defun make-c-completion-syntax-table ()
(let ((table (copy-syntax-table cmpl-standard-syntax-table))
(symbol-chars '(?>))
(separator-chars '(?+ ?* ?/ ?: ?%))
)
(dolist (char symbol-chars)
(modify-syntax-entry char "_" table))
(dolist (char separator-chars)
(modify-syntax-entry char " " table))
table))
(defun make-fortran-completion-syntax-table ()
(let ((table (copy-syntax-table cmpl-standard-syntax-table))
(separator-chars '(?+ ?- ?* ?/ ?:))
)
(dolist (char separator-chars)
(modify-syntax-entry char " " table))
table))
(defconst cmpl-lisp-syntax-table (make-lisp-completion-syntax-table))
(defconst cmpl-c-syntax-table (make-c-completion-syntax-table))
(defconst cmpl-fortran-syntax-table (make-fortran-completion-syntax-table))
(defvar cmpl-syntax-table cmpl-standard-syntax-table
"This variable holds the current completion syntax table.")
(make-variable-buffer-local 'cmpl-syntax-table)
(completion-advise lisp-mode-variables :after
(completion-setup-lisp-mode)
)
(completion-advise fortran-mode :after
(completion-setup-fortran-mode)
)
(add-hook 'c-mode-common-hook 'completion-setup-c-modes)
(add-hook 'c-mode-hook 'completion-setup-c-mode)
(add-hook 'c++-mode-hook 'completion-setup-c++-mode)
(defvar cmpl-symbol-start nil
"Set to the first character of the symbol after one of the completion
symbol functions is called.")
(defvar cmpl-symbol-end nil
"Set to the last character of the symbol after one of the completion
symbol functions is called.")
(defvar cmpl-saved-syntax nil)
(defvar cmpl-saved-point nil)
(defvar cmpl-preceding-syntax nil)
(defun symbol-under-point ()
"Returns the symbol that the point is currently on if it is longer
than *completion-min-length*."
(setq cmpl-saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(cond
((memq (char-syntax (following-char)) '(?w ?_))
(setq cmpl-saved-point (point)
cmpl-symbol-start (cmpl-scan-sexps (1+ cmpl-saved-point) -1)
cmpl-symbol-end (cmpl-scan-sexps cmpl-saved-point 1))
(cond ((= (char-syntax (char-after cmpl-symbol-start)) ?w)
(goto-char cmpl-symbol-start)
(forward-word 1)
(setq cmpl-symbol-start (point))
(goto-char cmpl-saved-point)
))
(cond ((= (char-syntax (char-after (1- cmpl-symbol-end))) ?w)
(goto-char cmpl-symbol-end)
(forward-word -1)
(setq cmpl-symbol-end (point))
(goto-char cmpl-saved-point)
))
(set-syntax-table cmpl-saved-syntax)
(if (and (<= (read-time-eval *completion-min-length*)
(- cmpl-symbol-end cmpl-symbol-start))
(<= (- cmpl-symbol-end cmpl-symbol-start)
(read-time-eval *completion-max-length*)))
(buffer-substring-no-properties cmpl-symbol-start cmpl-symbol-end))
)
(t
(set-syntax-table cmpl-saved-syntax)
nil)
))
(defun symbol-before-point ()
"Returns a string of the symbol immediately before point
or nil if there isn't one longer than *completion-min-length*."
(setq cmpl-saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(cond ((= (setq cmpl-preceding-syntax (char-syntax (preceding-char))) ?_)
(setq cmpl-symbol-end (point)
cmpl-symbol-start (cmpl-scan-sexps (1+ cmpl-symbol-end) -1)
)
(cond ((= (char-syntax (char-after cmpl-symbol-start)) ?w)
(goto-char cmpl-symbol-start)
(forward-word 1)
(setq cmpl-symbol-start (point))
(goto-char cmpl-symbol-end)
))
(set-syntax-table cmpl-saved-syntax)
(if (>= cmpl-symbol-end
(+ cmpl-symbol-start
(read-time-eval *completion-min-length*)))
(buffer-substring-no-properties cmpl-symbol-start
cmpl-symbol-end))
)
((= cmpl-preceding-syntax ?w)
(setq cmpl-saved-point (point)
cmpl-symbol-start (cmpl-scan-sexps (1+ cmpl-saved-point) -1))
(forward-word -1)
(setq cmpl-symbol-end (point))
(cond ((= (char-syntax (char-after cmpl-symbol-start)) ?w)
(goto-char cmpl-symbol-start)
(forward-word 1)
(setq cmpl-symbol-start (point))
))
(goto-char cmpl-saved-point)
(set-syntax-table cmpl-saved-syntax)
(if (and (<= (read-time-eval *completion-min-length*)
(- cmpl-symbol-end cmpl-symbol-start))
(<= (- cmpl-symbol-end cmpl-symbol-start)
(read-time-eval *completion-max-length*)))
(buffer-substring-no-properties cmpl-symbol-start
cmpl-symbol-end))
)
(t
(set-syntax-table cmpl-saved-syntax)
nil)
))
(defun symbol-under-or-before-point ()
(setq cmpl-saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(cond ((memq (char-syntax (following-char)) '(?w ?_))
(set-syntax-table cmpl-saved-syntax)
(symbol-under-point))
(t
(set-syntax-table cmpl-saved-syntax)
(symbol-before-point))
))
(defun symbol-before-point-for-complete ()
(setq cmpl-saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(cond ((memq (setq cmpl-preceding-syntax (char-syntax (preceding-char)))
'(?_ ?w))
(setq cmpl-symbol-end (point)
cmpl-symbol-start (cmpl-scan-sexps (1+ cmpl-symbol-end) -1)
)
(cond ((= (char-syntax (char-after cmpl-symbol-start)) ?w)
(goto-char cmpl-symbol-start)
(forward-word 1)
(setq cmpl-symbol-start (point))
(goto-char cmpl-symbol-end)
))
(set-syntax-table cmpl-saved-syntax)
(if (and (<= (read-time-eval
*completion-prefix-min-length*)
(- cmpl-symbol-end cmpl-symbol-start))
(<= (- cmpl-symbol-end cmpl-symbol-start)
(read-time-eval *completion-max-length*)))
(buffer-substring-no-properties cmpl-symbol-start
cmpl-symbol-end))
)
(t
(set-syntax-table cmpl-saved-syntax)
nil)
))
(defun symbol-before-point-for-cdabbrev ()
(setq cmpl-saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(cond ((memq (setq cmpl-preceding-syntax (char-syntax (preceding-char)))
'(?_ ?w))
(setq cmpl-symbol-end (point)
cmpl-symbol-start (cmpl-scan-sexps (1+ cmpl-symbol-end) -1)
)
(cond ((= (char-syntax (char-after cmpl-symbol-start)) ?w)
(goto-char cmpl-symbol-start)
(forward-word 1)
(setq cmpl-symbol-start (point))
(goto-char cmpl-symbol-end)
))
(set-syntax-table cmpl-saved-syntax)
(buffer-substring-no-properties cmpl-symbol-start cmpl-symbol-end)
)
(t
(set-syntax-table cmpl-saved-syntax)
nil)
))
(defmacro cmpl-statistics-block (&rest body)
"Only executes body if we are recording statistics."
(list 'cond
(list* '*record-cmpl-statistics-p* body)
))
(defvar completion-add-count-vector)
(defconst cmpl-source-unknown 0)
(defconst cmpl-source-init-file 1)
(defconst cmpl-source-file-parsing 2)
(defconst cmpl-source-separator 3)
(defconst cmpl-source-cursor-moves 4)
(defconst cmpl-source-interactive 5)
(defconst cmpl-source-cdabbrev 6)
(defconst num-cmpl-sources 7)
(defvar current-completion-source cmpl-source-unknown)
(defvar cdabbrev-completions-tried nil)
(defvar cdabbrev-current-point 0)
(defvar cdabbrev-current-window nil)
(defvar cdabbrev-wrapped-p nil)
(defvar cdabbrev-abbrev-string "")
(defvar cdabbrev-start-point 0)
(defvar cdabbrev-stop-point 0)
(defun reset-cdabbrev (abbrev-string &optional initial-completions-tried)
"Resets the cdabbrev search to search for abbrev-string.
initial-completions-tried is a list of downcased strings to ignore
during the search."
(setq cdabbrev-abbrev-string abbrev-string
cdabbrev-completions-tried
(cons (downcase abbrev-string) initial-completions-tried)
)
(reset-cdabbrev-window t)
)
(defun set-cdabbrev-buffer ()
(set-buffer (if (eq cdabbrev-current-window t)
(other-buffer)
(window-buffer cdabbrev-current-window)))
)
(defun reset-cdabbrev-window (&optional initializep)
"Resets the cdabbrev search to search for abbrev-string.
initial-completions-tried is a list of downcased strings to ignore
during the search."
(cond (initializep
(setq cdabbrev-current-window (selected-window))
)
((eq cdabbrev-current-window t)
(setq cdabbrev-current-window nil))
(cdabbrev-current-window
(setq cdabbrev-current-window (next-window cdabbrev-current-window))
(if (eq cdabbrev-current-window (selected-window))
(setq cdabbrev-current-window t)))
)
(when cdabbrev-current-window
(save-excursion
(set-cdabbrev-buffer)
(setq cdabbrev-current-point (point)
cdabbrev-start-point cdabbrev-current-point
cdabbrev-stop-point
(if *cdabbrev-radius*
(max (point-min)
(- cdabbrev-start-point *cdabbrev-radius*))
(point-min))
cdabbrev-wrapped-p nil)
)))
(defun next-cdabbrev ()
"Return the next possible cdabbrev expansion or nil if there isn't one.
reset-cdabbrev must've been called. This is sensitive to case-fold-search."
(when cdabbrev-current-window
(let (saved-point
saved-syntax
(expansion nil)
downcase-expansion tried-list syntax saved-point-2)
(save-excursion
(unwind-protect
(progn
(set-cdabbrev-buffer)
(setq saved-point (point)
saved-syntax (syntax-table))
(set-syntax-table cmpl-syntax-table)
(goto-char cdabbrev-current-point)
(while
(cond
(
(search-backward cdabbrev-abbrev-string cdabbrev-stop-point t)
(not
(and
(or (= (setq syntax (char-syntax (preceding-char))) ? )
(and (= syntax ?w)
(progn
(setq saved-point-2 (point))
(forward-word -1)
(prog1
(= (char-syntax (preceding-char)) ? )
(goto-char saved-point-2)
))))
(setq expansion (symbol-under-point))
(progn
(setq tried-list cdabbrev-completions-tried
downcase-expansion (downcase expansion))
(while (and tried-list
(not (string-equal downcase-expansion
(car tried-list))))
(setq tried-list (cdr tried-list))
)
(if tried-list
(setq expansion nil)
t)
))))
(cdabbrev-wrapped-p
nil)
(t
(goto-char (setq cdabbrev-current-point
(if *cdabbrev-radius*
(min (point-max) (+ cdabbrev-start-point *cdabbrev-radius*))
(point-max))))
(setq cdabbrev-wrapped-p t))
))
(cond (expansion
(setq cdabbrev-completions-tried
(cons downcase-expansion cdabbrev-completions-tried)
cdabbrev-current-point (point))))
)
(set-syntax-table saved-syntax)
(goto-char saved-point)
))
(cond (expansion)
(t (reset-cdabbrev-window)
(next-cdabbrev)))
)))
(defvar cdab-leave-point-at-start nil)
(defvar cdab-last-insert-length nil)
(defvar cdab-original-string nil)
(defun cdabbrev (&optional arg)
"Inserts a cdabbrev completion at point.
Point is left at end. Consective calls rotate through all possibilities.
Prefix args ::
control-u :: leave the point at the beginning of the completion rather
than at the end.
a number :: rotate through the possible completions by that amount
`-' :: same as -1 (insert previous completion)
"
(interactive "*p")
(cond ((eq last-command this-command)
(delete-region (- (point) cdab-last-insert-length) (point))
)
(t
(cond ((consp current-prefix-arg)
(setq arg 0)
(setq cdab-leave-point-at-start t)
)
(t
(setq cdab-leave-point-at-start nil)
))
(setq cdab-original-string (symbol-before-point-for-cdabbrev))
(cond ((not cdab-original-string)
(setq this-command 'failed-cdabbrev)
(error "To dynamically expand, the point must be after a symbol.")))
(cmpl-statistics-block
(note-cdabbrev-entered-afresh cdab-original-string))
(reset-cdabbrev cdab-original-string nil)
(delete-region cmpl-symbol-start cmpl-symbol-end)
))
(let* ((print-status-p
(and (>= (cmpl-baud-rate) *print-next-completion-speed-threshold*)
(not (minibuffer-window-selected-p))))
(insert-point (point))
(entry (next-cdabbrev))
)
(cond (entry
(insert (cmpl-merge-string-cases entry cdab-original-string))
(cond (cdab-leave-point-at-start
(setq cdab-last-insert-length (- insert-point (point)))
(goto-char insert-point))
(t
(setq cdab-last-insert-length (- (point) insert-point)))
)
(cmpl-statistics-block (note-cdabbrev-inserted entry))
)
(t
(insert cdab-original-string)
(if (and print-status-p (sit-for 0))
(message "No %sdynamic expansions."
(if (eq this-command last-command) "more " "")))
(cmpl-statistics-block (record-cdabbrev-failed))
(setq this-command 'failed-cdabbrev)
))))
(defconst cmpl-obarray-length 511)
(defvar cmpl-prefix-obarray (make-vector cmpl-obarray-length 0)
"An obarray used to store the downcased completion prefices.
Each symbol is bound to a list of completion entries.")
(defvar cmpl-obarray (make-vector cmpl-obarray-length 0)
"An obarray used to store the downcased completions.
Each symbol is bound to a single completion entry.")
(defmacro completion-string (completion-entry)
(list 'car completion-entry))
(defmacro completion-num-uses (completion-entry)
(list 'car (list 'cdr completion-entry)))
(defmacro completion-last-use-time (completion-entry)
(list 'nth 2 completion-entry))
(defmacro completion-source (completion-entry)
(list 'nth 3 completion-entry))
(defmacro set-completion-string (completion-entry string)
(list 'setcar completion-entry string))
(defmacro set-completion-num-uses (completion-entry num-uses)
(list 'setcar (list 'cdr completion-entry) num-uses))
(defmacro set-completion-last-use-time (completion-entry last-use-time)
(list 'setcar (list 'cdr (list 'cdr completion-entry)) last-use-time))
(defun make-completion (string)
"Returns a list of a completion entry."
(list (list string 0 nil current-completion-source)))
(defmacro cmpl-prefix-entry-head (prefix-entry)
(list 'car prefix-entry))
(defmacro cmpl-prefix-entry-tail (prefix-entry)
(list 'cdr prefix-entry))
(defmacro set-cmpl-prefix-entry-head (prefix-entry new-head)
(list 'setcar prefix-entry new-head))
(defmacro set-cmpl-prefix-entry-tail (prefix-entry new-tail)
(list 'setcdr prefix-entry new-tail))
(defun make-cmpl-prefix-entry (completion-entry-list)
"Makes a new prefix entry containing only completion-entry."
(cons completion-entry-list completion-entry-list))
(defun clear-all-completions ()
"Initializes the completion storage. All existing completions are lost."
(interactive)
(setq cmpl-prefix-obarray (make-vector cmpl-obarray-length 0))
(setq cmpl-obarray (make-vector cmpl-obarray-length 0))
(cmpl-statistics-block
(record-clear-all-completions))
)
(defvar cmpl-lac-return-completions)
(defun list-all-completions ()
"Returns a list of all the known completion entries."
(let ((cmpl-lac-return-completions nil))
(mapatoms 'list-all-completions-1 cmpl-prefix-obarray)
cmpl-lac-return-completions))
(defun list-all-completions-1 (prefix-symbol)
(if (boundp prefix-symbol)
(setq cmpl-lac-return-completions
(append (cmpl-prefix-entry-head (symbol-value prefix-symbol))
cmpl-lac-return-completions))))
(defun list-all-completions-by-hash-bucket ()
"Returns a list of lists of all the known completion entries organized by
hash bucket."
(let ((cmpl-lac-return-completions nil))
(mapatoms 'list-all-completions-by-hash-bucket-1 cmpl-prefix-obarray)
cmpl-lac-return-completions))
(defun list-all-completions-by-hash-bucket-1 (prefix-symbol)
(if (boundp prefix-symbol)
(setq cmpl-lac-return-completions
(cons (cmpl-prefix-entry-head (symbol-value prefix-symbol))
cmpl-lac-return-completions))))
(defvar completion-to-accept nil)
(defvar cmpl-db-downcase-string nil)
(defvar cmpl-db-symbol nil)
(defvar cmpl-db-prefix-symbol nil)
(defvar cmpl-db-entry nil)
(defvar cmpl-db-debug-p nil
"Set to T if you want to debug the database.")
(defun find-exact-completion (string)
"Returns the completion entry for string or nil.
Sets up cmpl-db-downcase-string and cmpl-db-symbol."
(and (boundp (setq cmpl-db-symbol
(intern (setq cmpl-db-downcase-string (downcase string))
cmpl-obarray)))
(symbol-value cmpl-db-symbol)
))
(defun find-cmpl-prefix-entry (prefix-string)
"Returns the prefix entry for string. Sets cmpl-db-prefix-symbol.
Prefix-string must be exactly *completion-prefix-min-length* long
and downcased. Sets up cmpl-db-prefix-symbol."
(and (boundp (setq cmpl-db-prefix-symbol
(intern prefix-string cmpl-prefix-obarray)))
(symbol-value cmpl-db-prefix-symbol)))
(defvar inside-locate-completion-entry nil)
(defun locate-completion-entry (completion-entry prefix-entry)
"Locates the completion entry. Returns a pointer to the element
before the completion entry or nil if the completion entry is at the head.
Must be called after find-exact-completion."
(let ((prefix-list (cmpl-prefix-entry-head prefix-entry))
next-prefix-list
)
(cond
((not (eq (car prefix-list) completion-entry))
(while (and prefix-list
(not (eq completion-entry
(car (setq next-prefix-list (cdr prefix-list)))
)))
(setq prefix-list next-prefix-list))
(cond (
prefix-list)
(cmpl-db-debug-p
(error "Completion entry exists but not on prefix list - %s"
completion-entry))
(inside-locate-completion-entry
(locate-completion-db-error))
(t
(set cmpl-db-symbol nil)
(locate-completion-entry-retry completion-entry)
))))))
(defun locate-completion-entry-retry (old-entry)
(let ((inside-locate-completion-entry t))
(add-completion (completion-string old-entry)
(completion-num-uses old-entry)
(completion-last-use-time old-entry))
(let* ((cmpl-entry (find-exact-completion (completion-string old-entry)))
(pref-entry
(if cmpl-entry
(find-cmpl-prefix-entry
(substring cmpl-db-downcase-string
0 *completion-prefix-min-length*))))
)
(if (and cmpl-entry pref-entry)
(locate-completion-entry cmpl-entry pref-entry)
(locate-completion-db-error))
)))
(defun locate-completion-db-error ()
(error "Completion database corrupted. Try M-x clear-all-completions. Send bug report.")
)
(defun add-completion-to-tail-if-new (string)
"If the string is not in the database it is added to the end of the
approppriate prefix list with num-uses = 0. The database is unchanged if it
is there. string must be longer than *completion-prefix-min-length*.
This must be very fast.
Returns the completion entry."
(or (find-exact-completion string)
(let (
(entry (make-completion string))
(prefix-entry (find-cmpl-prefix-entry
(substring cmpl-db-downcase-string 0
(read-time-eval
*completion-prefix-min-length*))))
)
(cond (prefix-entry
(setcdr (cmpl-prefix-entry-tail prefix-entry) entry)
(set-cmpl-prefix-entry-tail prefix-entry entry))
(t
(set cmpl-db-prefix-symbol (make-cmpl-prefix-entry entry))
))
(cmpl-statistics-block
(note-added-completion))
(set cmpl-db-symbol (car entry))
)))
(defun add-completion-to-head (string)
"If the string is not in the database it is added to the head of the
approppriate prefix list. Otherwise it is moved to the head of the list.
string must be longer than *completion-prefix-min-length*.
Updates the saved string with the supplied string.
This must be very fast.
Returns the completion entry."
(if completion-to-accept (accept-completion))
(if (setq cmpl-db-entry (find-exact-completion string))
(let* ((prefix-entry (find-cmpl-prefix-entry
(substring cmpl-db-downcase-string 0
(read-time-eval
*completion-prefix-min-length*))))
(splice-ptr (locate-completion-entry cmpl-db-entry prefix-entry))
(cmpl-ptr (cdr splice-ptr))
)
(set-completion-string cmpl-db-entry string)
(cond (splice-ptr
(or (setcdr splice-ptr (cdr cmpl-ptr))
(set-cmpl-prefix-entry-tail prefix-entry splice-ptr))
(setcdr cmpl-ptr (cmpl-prefix-entry-head prefix-entry))
(set-cmpl-prefix-entry-head prefix-entry cmpl-ptr)
))
cmpl-db-entry)
(let (
(entry (make-completion string))
(prefix-entry (find-cmpl-prefix-entry
(substring cmpl-db-downcase-string 0
(read-time-eval
*completion-prefix-min-length*))))
)
(cond (prefix-entry
(setcdr entry (cmpl-prefix-entry-head prefix-entry))
(set-cmpl-prefix-entry-head prefix-entry entry))
(t
(set cmpl-db-prefix-symbol (make-cmpl-prefix-entry entry))
))
(cmpl-statistics-block
(note-added-completion))
(set cmpl-db-symbol (car entry))
)))
(defun delete-completion (string)
"Deletes the completion from the database. string must be longer than
*completion-prefix-min-length*."
(if completion-to-accept (accept-completion))
(if (setq cmpl-db-entry (find-exact-completion string))
(let* ((prefix-entry (find-cmpl-prefix-entry
(substring cmpl-db-downcase-string 0
(read-time-eval
*completion-prefix-min-length*))))
(splice-ptr (locate-completion-entry cmpl-db-entry prefix-entry))
)
(set cmpl-db-symbol nil)
(cond (splice-ptr
(or (setcdr splice-ptr (cdr (cdr splice-ptr)))
(set-cmpl-prefix-entry-tail prefix-entry splice-ptr))
)
(t
(or (set-cmpl-prefix-entry-head
prefix-entry (cdr (cmpl-prefix-entry-head prefix-entry)))
(set cmpl-db-prefix-symbol nil))
))
(cmpl-statistics-block
(note-completion-deleted))
)
(error "Unknown completion: %s. Couldn't delete it." string)
))
(defun interactive-completion-string-reader (prompt)
(let* ((default (symbol-under-or-before-point))
(new-prompt
(if default
(format "%s: (default: %s) " prompt default)
(format "%s: " prompt))
)
(read (completing-read new-prompt cmpl-obarray))
)
(if (zerop (length read)) (setq read (or default "")))
(list read)
))
(defun check-completion-length (string)
(if (< (length string) *completion-min-length*)
(error "The string \"%s\" is too short to be saved as a completion."
string)
(list string)))
(defun add-completion (string &optional num-uses last-use-time)
"If the string is not there, it is added to the head of the completion list.
Otherwise, it is moved to the head of the list.
The completion is altered appropriately if num-uses and/or last-use-time is
specified."
(interactive (interactive-completion-string-reader "Completion to add"))
(check-completion-length string)
(let* ((current-completion-source (if (interactive-p)
cmpl-source-interactive
current-completion-source))
(entry (add-completion-to-head string)))
(if num-uses (set-completion-num-uses entry num-uses))
(if last-use-time
(set-completion-last-use-time entry last-use-time))
))
(defun add-permanent-completion (string)
"Adds string if it isn't already there and and makes it a permanent string."
(interactive
(interactive-completion-string-reader "Completion to add permanently"))
(let ((current-completion-source (if (interactive-p)
cmpl-source-interactive
current-completion-source))
)
(add-completion string nil t)
))
(defun kill-completion (string)
(interactive (interactive-completion-string-reader "Completion to kill"))
(check-completion-length string)
(delete-completion string)
)
(defun accept-completion ()
"Accepts the pending completion in completion-to-accept.
This bumps num-uses. Called by add-completion-to-head and
completion-search-reset."
(let ((string completion-to-accept)
(current-completion-source cmpl-source-cdabbrev)
entry
)
(setq completion-to-accept nil)
(setq entry (add-completion-to-head string))
(set-completion-num-uses entry (1+ (completion-num-uses entry)))
(setq cmpl-completions-accepted-p t)
))
(defun use-completion-under-point ()
"Call this to add the completion symbol underneath the point into
the completion buffer."
(let ((string (and *completep* (symbol-under-point)))
(current-completion-source cmpl-source-cursor-moves))
(if string (add-completion-to-head string))))
(defun use-completion-before-point ()
"Call this to add the completion symbol before point into
the completion buffer."
(let ((string (and *completep* (symbol-before-point)))
(current-completion-source cmpl-source-cursor-moves))
(if string (add-completion-to-head string))))
(defun use-completion-under-or-before-point ()
"Call this to add the completion symbol before point into
the completion buffer."
(let ((string (and *completep* (symbol-under-or-before-point)))
(current-completion-source cmpl-source-cursor-moves))
(if string (add-completion-to-head string))))
(defun use-completion-before-separator ()
"Call this to add the completion symbol before point into
the completion buffer. Completions added this way will automatically be
saved if *separator-character-uses-completion-p* is non-nil."
(let ((string (and *completep* (symbol-before-point)))
(current-completion-source cmpl-source-separator)
entry)
(cmpl-statistics-block
(note-separator-character string)
)
(cond (string
(setq entry (add-completion-to-head string))
(when (and *separator-character-uses-completion-p*
(zerop (completion-num-uses entry)))
(set-completion-num-uses entry 1)
(setq cmpl-completions-accepted-p t)
)))
))
(defvar cmpl-test-string "")
(defvar cmpl-test-regexp "")
(defvar cmpl-last-index 0)
(defvar cmpl-cdabbrev-reset-p nil)
(defvar cmpl-next-possibilities nil)
(defvar cmpl-starting-possibilities nil)
(defvar cmpl-next-possibility nil)
(defvar cmpl-tried-list nil)
(defun completion-search-reset (string)
"Given a string, sets up the get-completion and completion-search-next functions.
String must be longer than *completion-prefix-min-length*."
(if completion-to-accept (accept-completion))
(setq cmpl-starting-possibilities
(cmpl-prefix-entry-head
(find-cmpl-prefix-entry
(downcase (substring string 0 *completion-prefix-min-length*))))
cmpl-test-string string
cmpl-test-regexp (concat (regexp-quote string) "."))
(completion-search-reset-1)
)
(defun completion-search-reset-1 ()
(setq cmpl-next-possibilities cmpl-starting-possibilities
cmpl-next-possibility nil
cmpl-cdabbrev-reset-p nil
cmpl-last-index -1
cmpl-tried-list nil
))
(defun completion-search-next (index)
"Returns the next completion entry. If index is out of sequence it resets
and starts from the top. If there are no more entries it tries cdabbrev and
returns only a string."
(cond
((= index (setq cmpl-last-index (1+ cmpl-last-index)))
(completion-search-peek t))
((minusp index)
(completion-search-reset-1)
(setq cmpl-last-index index)
(setq cmpl-next-possibilities (reverse cmpl-starting-possibilities))
(while (and (completion-search-peek nil)
(minusp (setq index (1+ index))))
(setq cmpl-next-possibility nil)
)
(cond ((not cmpl-next-possibilities))
((= -1 cmpl-last-index)
(setq cmpl-next-possibilities cmpl-starting-possibilities))
(t
(setq cmpl-next-possibilities
(nthcdr (- (length cmpl-starting-possibilities)
(length cmpl-next-possibilities))
cmpl-starting-possibilities))
)))
(t
(completion-search-reset-1)
(setq cmpl-last-index index)
(while (and (completion-search-peek t)
(not (minusp (setq index (1- index)))))
(setq cmpl-next-possibility nil)
))
)
(prog1
cmpl-next-possibility
(setq cmpl-next-possibility nil)
))
(defun completion-search-peek (use-cdabbrev)
"Returns the next completion entry without actually moving the pointers.
Calling this again or calling completion-search-next will result in the same
string being returned. Depends on case-fold-search.
If there are no more entries it tries cdabbrev and then returns only a string."
(cond
(cmpl-next-possibility)
((and cmpl-next-possibilities
(progn
(while
(and (not (eq 0 (string-match cmpl-test-regexp
(completion-string (car cmpl-next-possibilities)))))
(setq cmpl-next-possibilities (cdr cmpl-next-possibilities))
))
cmpl-next-possibilities
))
(setq cmpl-next-possibility (car cmpl-next-possibilities)
cmpl-tried-list (cons (downcase (completion-string cmpl-next-possibility))
cmpl-tried-list)
cmpl-next-possibilities (cdr cmpl-next-possibilities)
)
cmpl-next-possibility)
(use-cdabbrev
(cond ((not cmpl-cdabbrev-reset-p)
(reset-cdabbrev cmpl-test-string cmpl-tried-list)
(setq cmpl-cdabbrev-reset-p t)
))
(setq cmpl-next-possibility (next-cdabbrev))
)
))
(defun completion-mode ()
"Toggles whether or not new words are added to the database."
(interactive)
(setq *completep* (not *completep*))
(message "Completion mode is now %s." (if *completep* "ON" "OFF"))
)
(defvar cmpl-current-index 0)
(defvar cmpl-original-string nil)
(defvar cmpl-last-insert-length nil)
(defvar cmpl-leave-point-at-start nil)
(defun complete (&optional arg)
"Inserts a completion at point.
Point is left at end. Consective calls rotate through all possibilities.
Prefix args ::
control-u :: leave the point at the beginning of the completion rather
than at the end.
a number :: rotate through the possible completions by that amount
`-' :: same as -1 (insert previous completion)
{See the comments at the top of completion.el for more info.}
"
(interactive "*p")
(cond ((eq last-command this-command)
(delete-region (- (point) cmpl-last-insert-length) (point))
(setq cmpl-current-index (+ cmpl-current-index (or arg 1)))
)
(t
(if (not cmpl-initialized-p)
(initialize-completions))
(cond ((consp current-prefix-arg)
(setq arg 0)
(setq cmpl-leave-point-at-start t)
)
(t
(setq cmpl-leave-point-at-start nil)
))
(setq cmpl-original-string (symbol-before-point-for-complete))
(cond ((not cmpl-original-string)
(setq this-command 'failed-complete)
(error "To complete, the point must be after a symbol at least %d characters long."
*completion-prefix-min-length*)))
(setq cmpl-current-index (if current-prefix-arg arg 0))
(cmpl-statistics-block
(note-complete-entered-afresh cmpl-original-string))
(completion-search-reset cmpl-original-string)
(delete-region cmpl-symbol-start cmpl-symbol-end)
))
(let* ((print-status-p
(and (>= (cmpl-baud-rate) *print-next-completion-speed-threshold*)
(not (minibuffer-window-selected-p))))
(insert-point (point))
(entry (completion-search-next cmpl-current-index))
string
)
(cond (entry
(setq string (if (stringp entry)
entry (completion-string entry)))
(setq string (cmpl-merge-string-cases
string cmpl-original-string))
(insert string)
(setq completion-to-accept string)
(cond (cmpl-leave-point-at-start
(setq cmpl-last-insert-length (- insert-point (point)))
(goto-char insert-point))
(t
(setq cmpl-last-insert-length (- (point) insert-point)))
)
(cmpl-statistics-block
(note-complete-inserted entry cmpl-current-index))
(cond
((and print-status-p
(sit-for 0)
(setq entry
(completion-search-peek
*print-next-completion-does-cdabbrev-search-p*)))
(setq string (if (stringp entry)
entry (completion-string entry)))
(setq string (cmpl-merge-string-cases
string cmpl-original-string))
(message "Next completion: %s" string)
))
)
(t
(insert cmpl-original-string)
(setq completion-to-accept nil)
(if (and print-status-p (sit-for 0))
(message "No %scompletions."
(if (eq this-command last-command) "more " "")))
(cmpl-statistics-block
(record-complete-failed cmpl-current-index))
(setq this-command 'failed-complete)
))))
(if (fboundp 'key-for-others-chord)
(condition-case e
(global-set-key (key-for-others-chord "return" '(control)) 'complete)
(error)
))
(if (fboundp 'gmacs-keycode)
(global-set-key (gmacs-keycode "return" '(control)) 'complete)
)
(global-set-key "\M-\r" 'complete)
(global-set-key "\M-/" 'cdabbrev)
(defun add-completions-from-file (file)
"Parses all the definition names from a Lisp mode file and adds them to the
completion database."
(interactive "fFile: ")
(setq file (if (fboundp 'expand-file-name-defaulting)
(expand-file-name-defaulting file)
(expand-file-name file)))
(let* ((buffer (get-file-buffer file))
(buffer-already-there-p buffer)
)
(when (not buffer-already-there-p)
(let ((*modes-for-completion-find-file-hook* nil))
(setq buffer (find-file-noselect file))
))
(unwind-protect
(save-excursion
(set-buffer buffer)
(add-completions-from-buffer)
)
(when (not buffer-already-there-p)
(kill-buffer buffer))
)))
(defun add-completions-from-buffer ()
(interactive)
(let ((current-completion-source cmpl-source-file-parsing)
(start-num
(cmpl-statistics-block
(aref completion-add-count-vector cmpl-source-file-parsing)))
mode
)
(cond ((memq major-mode *completion-find-file-hook-lisp-major-modes*)
(add-completions-from-lisp-buffer)
(setq mode 'lisp)
)
((memq major-mode *completion-find-file-hook-c-major-modes*)
(add-completions-from-c-buffer)
(setq mode 'c)
)
(t
(error "Do not know how to parse completions in %s buffers."
major-mode)
))
(cmpl-statistics-block
(record-cmpl-parse-file
mode (point-max)
(- (aref completion-add-count-vector cmpl-source-file-parsing)
start-num)))
))
(defun cmpl-find-file-hook ()
(cond (*completep*
(cond ((and (memq major-mode
*completion-find-file-hook-lisp-major-modes*)
(memq 'lisp *modes-for-completion-find-file-hook*)
)
(add-completions-from-buffer))
((and (memq major-mode *completion-find-file-hook-c-major-modes*)
(memq 'c *modes-for-completion-find-file-hook*)
)
(add-completions-from-buffer)
)))
))
(if (boundp 'find-file-hook)
(pushnew 'cmpl-find-file-hook find-file-hook)
(pushnew 'cmpl-find-file-hook find-file-hooks))
(defun add-completions-from-tags-table ()
"Add completions from the current tags-table-buffer."
(interactive)
(visit-tags-table-buffer)
(save-excursion
(goto-char (point-min))
(let (string)
(condition-case e
(while t
(search-forward "\177")
(backward-char 3)
(and (setq string (symbol-under-point))
(add-completion-to-tail-if-new string))
(forward-char 3)
)
(search-failed)
))))
(defconst *lisp-def-regexp*
"\n(\\(\\w*:\\)?def\\(\\w\\|\\s_\\)*\\s +(*"
"A regexp that searches for lisp definition form."
)
(defun add-completions-from-lisp-buffer ()
"Parses all the definition names from a Lisp mode buffer and adds them to
the completion database."
(let (string)
(save-excursion
(goto-char (point-min))
(condition-case e
(while t
(re-search-forward *lisp-def-regexp*)
(and (setq string (symbol-under-point))
(add-completion-to-tail-if-new string))
)
(search-failed)
))))
(defun make-c-def-completion-syntax-table ()
(let ((table (copy-syntax-table))
(whitespace-chars '(? ?\n ?\t ?\f ?\v ?\r))
(separator-chars '(?, ?* ?= ?\( ?\
))
)
(dotimes (i 256)
(modify-syntax-entry i "w" table))
(dolist (char whitespace-chars)
(modify-syntax-entry char "_" table))
(dolist (char separator-chars)
(modify-syntax-entry char " " table))
(modify-syntax-entry ?\[ "(]" table)
(modify-syntax-entry ?\{ "(}" table)
(modify-syntax-entry ?\] ")[" table)
(modify-syntax-entry ?\} "){" table)
table))
(defconst cmpl-c-def-syntax-table (make-c-def-completion-syntax-table))
(defconst *c-def-regexp*
"\n[_a-zA-Z#]"
"A regexp that searches for a definition form."
)
(defun add-completions-from-c-buffer ()
"Parses all the definition names from a C mode buffer and adds them to the
completion database."
(let (string next-point char
(saved-syntax (syntax-table))
)
(save-excursion
(goto-char (point-min))
(catch 'finish-add-completions
(unwind-protect
(while t
(set-syntax-table cmpl-c-def-syntax-table)
(condition-case e
(while t
(re-search-forward *c-def-regexp*)
(cond
((= (preceding-char) ?#)
(setq string (buffer-substring-no-properties
(point)
(+ (point) 6)))
(cond ((or (string-equal string "define")
(string-equal string "ifdef ")
)
(and (forward-word 2)
(setq string (symbol-before-point))
(add-completion-to-tail-if-new string)
))))
(t
(setq next-point (point))
(while (and
next-point
(setq next-point (cmpl-scan-sexps next-point 1))
)
(goto-char next-point)
(while (= (setq char (following-char)) ?*)
(goto-char
(setq next-point (cmpl-scan-sexps (point) 1)))
)
(forward-word -1)
(if (setq string (symbol-under-point))
(add-completion-to-tail-if-new string)
(if (and (looking-at "_AP")
(progn
(forward-word -1)
(setq string
(symbol-under-point))
))
(add-completion-to-tail-if-new string)
)
)
(goto-char next-point)
(if (= (char-syntax char) ?\()
(while (= (char-syntax char) ?\()
(setq next-point (cmpl-scan-sexps next-point 1)
char (char-after next-point))
)
(or (= char ?,)
(setq next-point nil)
))
))))
(search-failed
(throw 'finish-add-completions t)
)
(error
(if (or (string-equal (second e)
"Containing expression ends prematurely")
(string-equal (second e) "Unbalanced parentheses"))
(forward-line 1)
(message "Error parsing C buffer for completions. Please bug report.")
(throw 'finish-add-completions t)
))
))
(set-syntax-table saved-syntax)
)))))
(defun kill-emacs-save-completions ()
"The version of save-completions-to-file called at kill-emacs
time."
(when (and *save-completions-p* *completep* cmpl-initialized-p)
(cond
((not cmpl-completions-accepted-p)
(message "Completions database has not changed - not writing."))
(t
(save-completions-to-file)
))
))
(defconst saved-cmpl-file-header
";;; Completion Initialization file.
;;; Version = %s
;;; Format is (<string> . <last-use-time>)
;;; <string> is the completion
;;; <last-use-time> is the time the completion was last used
;;; If it is t, the completion will never be pruned from the file.
;;; Otherwise it is in hours since 1900.
\n")
(defun completion-backup-filename (filename)
(let* ((dirname (file-name-directory filename))
(base-name (file-name-nondirectory filename))
(short-name (if (> (length base-name) 10)
(substring base-name 0 10)
base-name)))
(concat dirname short-name ".BAK")))
(defun save-completions-to-file (&optional filename)
"Saves a completion init file. If file is not specified,
then *saved-completions-filename* is used."
(interactive)
(setq filename (expand-file-name (or filename *saved-completions-filename*)))
(when (file-writable-p filename)
(if (not cmpl-initialized-p)
(initialize-completions))
(message "Saving completions to file %s" filename)
(let* ((trim-versions-without-asking t)
(kept-old-versions 0)
(kept-new-versions *completion-file-versions-kept*)
last-use-time
(current-time (cmpl-hours-since-1900))
(total-in-db 0)
(total-perm 0)
(total-saved 0)
(backup-filename (completion-backup-filename filename))
)
(save-excursion
(get-buffer-create " *completion-save-buffer*")
(set-buffer " *completion-save-buffer*")
(setq buffer-file-name filename)
(when (not (verify-visited-file-modtime (current-buffer)))
(message "Completion file has changed. Merging. . .")
(load-completions-from-file filename t)
(message "Merging finished. Saving completions to file %s" filename)
)
(clear-visited-file-modtime)
(erase-buffer)
(insert (format saved-cmpl-file-header *completion-version*))
(dolist (completion (list-all-completions))
(setq total-in-db (1+ total-in-db))
(setq last-use-time (completion-last-use-time completion))
(cond ((or
(and (eq last-use-time t)
(setq total-perm (1+ total-perm)))
(if (plusp (completion-num-uses completion))
(setq last-use-time current-time)
(and last-use-time
(or (not *saved-completion-retention-time*)
(< (- current-time last-use-time)
*saved-completion-retention-time*))
)))
(setq total-saved (1+ total-saved))
(insert (prin1-to-string (cons (completion-string completion)
last-use-time)) "\n")
)))
(condition-case e
(let ((file-exists-p (file-exists-p filename)))
(when file-exists-p
(unless (file-exists-p backup-filename)
(rename-file filename backup-filename))
(copy-file backup-filename filename t)
)
(save-buffer)
(when file-exists-p
(delete-file backup-filename)
))
(error
(set-buffer-modified-p nil)
(message "Couldn't save completion file %s." filename)
))
(setq cmpl-completions-accepted-p nil)
)
(cmpl-statistics-block
(record-save-completions total-in-db total-perm total-saved))
)))
(defun autosave-completions ()
(when (and *save-completions-p* *completep* cmpl-initialized-p
*completion-auto-save-period*
(> cmpl-emacs-idle-time *completion-auto-save-period*)
cmpl-completions-accepted-p)
(save-completions-to-file)
))
(pushnew 'autosave-completions cmpl-emacs-idle-time-hooks)
(defun load-completions-from-file (&optional filename no-message-p)
"loads a completion init file. If file is not specified,
then *saved-completions-filename* is used"
(interactive)
(setq filename (expand-file-name (or filename *saved-completions-filename*)))
(let* ((backup-filename (completion-backup-filename filename))
(backup-readable-p (file-readable-p backup-filename))
)
(when backup-readable-p (setq filename backup-filename))
(when (file-readable-p filename)
(if (not no-message-p)
(message "Loading completions from %sfile %s . . ."
(if backup-readable-p "backup " "") filename))
(save-excursion
(get-buffer-create " *completion-save-buffer*")
(set-buffer " *completion-save-buffer*")
(setq buffer-file-name filename)
(clear-visited-file-modtime)
(erase-buffer)
(let ((insert-okay-p nil)
(buffer (current-buffer))
(current-time (cmpl-hours-since-1900))
string num-uses entry last-use-time
cmpl-entry cmpl-last-use-time
(current-completion-source cmpl-source-init-file)
(start-num
(cmpl-statistics-block
(aref completion-add-count-vector cmpl-source-file-parsing)))
(total-in-file 0) (total-perm 0)
)
(condition-case e
(progn (insert-file-contents filename t)
(setq insert-okay-p t))
(file-error
(message "File error trying to load completion file %s."
filename)))
(when insert-okay-p
(goto-char (point-min))
(condition-case e
(while t
(setq entry (read buffer))
(setq total-in-file (1+ total-in-file))
(cond
((and (consp entry)
(stringp (setq string (car entry)))
(cond
((eq (setq last-use-time (cdr entry)) 'T)
(setq total-perm (1+ total-perm))
(setq last-use-time t))
((eq last-use-time t)
(setq total-perm (1+ total-perm)))
((integerp last-use-time))
))
(setq cmpl-last-use-time
(completion-last-use-time
(setq cmpl-entry
(add-completion-to-tail-if-new string))
))
(if (or (eq last-use-time t)
(and (> last-use-time 1000)
(not (eq cmpl-last-use-time t))
(or (not cmpl-last-use-time)
(> last-use-time cmpl-last-use-time))
))
(set-completion-last-use-time cmpl-entry last-use-time)
))
(t
(message "Error: invalid saved completion - %s"
(prin1-to-string entry))
(search-forward "\n(")
)))
(search-failed
(message "End of file while reading completions.")
)
(end-of-file
(if (= (point) (point-max))
(if (not no-message-p)
(message "Loading completions from file %s . . . Done."
filename))
(message "End of file while reading completions.")
))
))
(cmpl-statistics-block
(record-load-completions
total-in-file total-perm
(- (aref completion-add-count-vector cmpl-source-init-file)
start-num)))
)))))
(defun initialize-completions ()
"Loads the default completions file and sets up so that exiting emacs will
automatically save the file."
(interactive)
(condition-case e
(init-cmpl-emacs-idle-process)
(error
(message "Error starting completion idle process: %s." e)
(sit-for 0)
))
(cond ((not cmpl-initialized-p)
(load-completions-from-file)
))
(setq cmpl-initialized-p t)
)
(completion-advise kill-emacs :before
(kill-emacs-save-completions)
(cmpl-statistics-block
(record-cmpl-kill-emacs))
)
(defvar $$$cmpl-old-kill-region (symbol-function 'kill-region))
(defun kill-region (&optional beg end &optional yank-handler)
"Kill (\"cut\") text between point and mark.
This deletes the text from the buffer and saves it in the kill ring.
The command \\[yank] can retrieve it from there.
(If you want to kill and then yank immediately, use \\[kill-ring-save].)
If you want to append the killed region to the last killed text,
use \\[append-next-kill] before \\[kill-region].
If the buffer is read-only, Emacs will beep and refrain from deleting
the text, but put the text in the kill ring anyway. This means that
you can use the killing commands to copy text from a read-only buffer.
This is the primitive for programs to kill text (as opposed to deleting it).
Supply two arguments, character positions indicating the stretch of text
to be killed.
Any command that calls this function is a \"kill command\".
If the previous command was also a kill command,
the text killed this time appends to the text killed last time
to make one entry in the kill ring.
In Lisp code, optional third arg YANK-HANDLER, if non-nil,
specifies the yank-handler text property to be set on the killed
text. See `insert-for-yank'.
Patched to remove the most recent completion."
(interactive "*")
(cond ((and (eq last-command 'complete) (eq last-command-char ?\C-w))
(delete-region (point) (- (point) cmpl-last-insert-length))
(insert cmpl-original-string)
(setq completion-to-accept nil)
(cmpl-statistics-block
(record-complete-failed))
)
((and (eq last-command 'cdabbrev) (eq last-command-char ?\C-w))
(delete-region (point) (- (point) cdab-last-insert-length))
(insert cdab-original-string)
(cmpl-statistics-block
(record-cdabbrev-failed))
)
(t
(if (not beg)
(setq beg (min (point) (mark))
end (max (point) (mark)))
)
(funcall $$$cmpl-old-kill-region beg end yank-handler)
)))
(defun completion-separator-self-insert-command (arg)
(interactive "p")
(use-completion-before-separator)
(self-insert-command arg)
)
(defun completion-separator-self-insert-autofilling (arg)
(interactive "p")
(use-completion-before-separator)
(self-insert-command arg)
(and (> (current-column) fill-column)
(or (and (boundp 'auto-fill-hook)
(symbol-value 'auto-fill-hook)
(funcall (symbol-value 'auto-fill-hook)))
(and (boundp 'auto-fill-function)
auto-fill-function
(funcall auto-fill-function))
))
)
(defmacro def-completion-wrapper (function-name type &optional new-name)
"Add a call to update the completion database before the function is
executed. TYPE is the type of the wrapper to be added. Can be :before or
:under."
(completion-advise-1
function-name ':before
(ecase type
(:before '((use-completion-before-point)))
(:separator '((use-completion-before-separator)))
(:under '((use-completion-under-point)))
(:under-or-before
'((use-completion-under-or-before-point)))
(:minibuffer-separator
'((let ((cmpl-syntax-table cmpl-standard-syntax-table))
(use-completion-before-separator))))
)
new-name
))
(if (>= cmpl-emacs-major-version 19)
(global-set-key " " 'completion-separator-self-insert-command)
(global-set-key " " 'completion-separator-self-insert-autofilling))
(global-set-key "!" 'completion-separator-self-insert-command)
(global-set-key "%" 'completion-separator-self-insert-command)
(global-set-key "^" 'completion-separator-self-insert-command)
(global-set-key "&" 'completion-separator-self-insert-command)
(global-set-key "(" 'completion-separator-self-insert-command)
(global-set-key ")" 'completion-separator-self-insert-command)
(global-set-key "=" 'completion-separator-self-insert-command)
(global-set-key "`" 'completion-separator-self-insert-command)
(global-set-key "|" 'completion-separator-self-insert-command)
(global-set-key "{" 'completion-separator-self-insert-command)
(global-set-key "}" 'completion-separator-self-insert-command)
(global-set-key "[" 'completion-separator-self-insert-command)
(global-set-key "]" 'completion-separator-self-insert-command)
(global-set-key ";" 'completion-separator-self-insert-command)
(global-set-key "\"" 'completion-separator-self-insert-command)
(global-set-key "'" 'completion-separator-self-insert-command)
(global-set-key "#" 'completion-separator-self-insert-command)
(global-set-key "," 'completion-separator-self-insert-command)
(global-set-key "?" 'completion-separator-self-insert-command)
(global-set-key "." 'completion-separator-self-insert-command)
(global-set-key ":" 'completion-separator-self-insert-command)
(defvar cmpl-lisp-installed-p nil)
(defun completion-setup-lisp-mode ()
(unless cmpl-lisp-installed-p
(setq cmpl-lisp-installed-p t)
(define-key lisp-mode-map "!" 'self-insert-command)
(define-key lisp-mode-map "&" 'self-insert-command)
(define-key lisp-mode-map "%" 'self-insert-command)
(define-key lisp-mode-map "?" 'self-insert-command)
(define-key lisp-mode-map "=" 'self-insert-command)
(define-key lisp-mode-map "^" 'self-insert-command)
)
(setq cmpl-syntax-table cmpl-lisp-syntax-table)
)
(defvar cmpl-c-installed-p nil)
(defun completion-setup-c-modes ()
(unless cmpl-c-installed-p
(setq cmpl-c-installed-p t)
(if (function-defined-and-loaded 'c-electric-semi&comma)
(def-completion-wrapper c-electric-semi&comma :separator))
(if (function-defined-and-loaded 'electric-c-semi)
(def-completion-wrapper electric-c-semi :separator))
)
)
(defun completion-setup-c-mode ()
(define-key c-mode-map "+" 'completion-separator-self-insert-command)
(define-key c-mode-map "*" 'completion-separator-self-insert-command)
(define-key c-mode-map "/" 'completion-separator-self-insert-command)
(setq cmpl-syntax-table cmpl-c-syntax-table)
)
(defun completion-setup-c++-mode ()
(define-key c++-mode-map "+" 'completion-separator-self-insert-command)
(define-key c++-mode-map "*" 'completion-separator-self-insert-command)
(define-key c++-mode-map "/" 'completion-separator-self-insert-command)
(setq cmpl-syntax-table cmpl-c-syntax-table)
)
(defvar cmpl-fortran-installed-p nil)
(defun completion-setup-fortran-mode ()
(unless cmpl-fortran-installed-p
(setq cmpl-fortran-installed-p t)
(define-key fortran-mode-map "+" 'completion-separator-self-insert-command)
(define-key fortran-mode-map "-" 'completion-separator-self-insert-command)
(define-key fortran-mode-map "*" 'completion-separator-self-insert-command)
(define-key fortran-mode-map "/" 'completion-separator-self-insert-command)
)
(setq cmpl-syntax-table cmpl-fortran-syntax-table)
)
(def-completion-wrapper newline :separator)
(def-completion-wrapper newline-and-indent :separator)
(if (function-defined-and-loaded 'shell-send-input)
(def-completion-wrapper shell-send-input :separator))
(if (function-defined-and-loaded 'comint-send-input)
(def-completion-wrapper comint-send-input :separator))
(def-completion-wrapper exit-minibuffer :minibuffer-separator)
(def-completion-wrapper eval-print-last-sexp :separator)
(def-completion-wrapper eval-last-sexp :separator)
(def-completion-wrapper next-line :under-or-before)
(def-completion-wrapper previous-line :under-or-before)
(def-completion-wrapper beginning-of-buffer :under-or-before)
(def-completion-wrapper end-of-buffer :under-or-before)
(defun cmpl-beginning-of-line (&optional n)
"Move point to beginning of current line.\n\
With argument ARG not nil or 1, move forward ARG - 1 lines first.\n\
If scan reaches end of buffer, stop there without error."
(interactive "p")
(use-completion-under-or-before-point)
(beginning-of-line n)
)
(defun cmpl-end-of-line (&optional n)
"Move point to end of current line.\n\
With argument ARG not nil or 1, move forward ARG - 1 lines first.\n\
If scan reaches end of buffer, stop there without error."
(interactive "p")
(use-completion-under-or-before-point)
(end-of-line n)
)
(defun cmpl-forward-char (n)
"Move point right ARG characters (left if ARG negative).\n\
On reaching end of buffer, stop and signal error."
(interactive "p")
(use-completion-under-or-before-point)
(forward-char n)
)
(defun cmpl-backward-char (n)
"Move point left ARG characters (right if ARG negative).\n\
On attempt to pass beginning or end of buffer, stop and signal error."
(interactive "p")
(use-completion-under-point)
(if (eq last-command 'complete)
(cmpl-statistics-block (record-complete-failed)))
(backward-char n)
)
(defun cmpl-forward-word (n)
"Move point forward ARG words (backward if ARG is negative).\n\
Normally returns t.\n\
If an edge of the buffer is reached, point is left there\n\
and nil is returned."
(interactive "p")
(use-completion-under-or-before-point)
(forward-word n)
)
(defun cmpl-backward-word (n)
"Move backward until encountering the end of a word.
With argument, do this that many times.
In programs, it is faster to call forward-word with negative arg."
(interactive "p")
(use-completion-under-point)
(if (eq last-command 'complete)
(cmpl-statistics-block (record-complete-failed)))
(forward-word (- n))
)
(defun cmpl-forward-sexp (n)
"Move forward across one balanced expression.
With argument, do this that many times."
(interactive "p")
(use-completion-under-or-before-point)
(forward-sexp n)
)
(defun cmpl-backward-sexp (n)
"Move backward across one balanced expression.
With argument, do this that many times."
(interactive "p")
(use-completion-under-point)
(if (eq last-command 'complete)
(cmpl-statistics-block (record-complete-failed)))
(backward-sexp n)
)
(defun cmpl-delete-backward-char (n killflag)
"Delete the previous ARG characters (following, with negative ARG).\n\
Optional second arg KILLFLAG non-nil means kill instead (save in kill ring).\n\
Interactively, ARG is the prefix arg, and KILLFLAG is set if\n\
ARG was explicitly specified."
(interactive "p\nP")
(if (eq last-command 'complete)
(cmpl-statistics-block (record-complete-failed)))
(delete-backward-char n killflag)
)
(defvar $$$cmpl-old-backward-delete-char-untabify
(symbol-function 'backward-delete-char-untabify))
(defun backward-delete-char-untabify (arg &optional killp)
"Delete characters backward, changing tabs into spaces.
Delete ARG chars, and kill (save in kill ring) if KILLP is non-nil.
Interactively, ARG is the prefix arg (default 1)
and KILLP is t if prefix arg is was specified."
(interactive "*p\nP")
(if (eq last-command 'complete)
(cmpl-statistics-block (record-complete-failed)))
(funcall $$$cmpl-old-backward-delete-char-untabify arg killp)
)
(global-set-key "\C-?" 'cmpl-delete-backward-char)
(global-set-key "\M-\C-F" 'cmpl-forward-sexp)
(global-set-key "\M-\C-B" 'cmpl-backward-sexp)
(global-set-key "\M-F" 'cmpl-forward-word)
(global-set-key "\M-B" 'cmpl-backward-word)
(global-set-key "\C-F" 'cmpl-forward-char)
(global-set-key "\C-B" 'cmpl-backward-char)
(global-set-key "\C-A" 'cmpl-beginning-of-line)
(global-set-key "\C-E" 'cmpl-end-of-line)
(def-completion-wrapper electric-buffer-list :under-or-before)
(def-completion-wrapper list-buffers :under-or-before)
(def-completion-wrapper scroll-up :under-or-before)
(def-completion-wrapper scroll-down :under-or-before)
(def-completion-wrapper execute-extended-command
:under-or-before)
(def-completion-wrapper other-window :under-or-before)
(if (fboundp 'up-ten-lines)
(def-completion-wrapper up-ten-lines :under-or-before))
(if (fboundp 'down-ten-lines)
(def-completion-wrapper down-ten-lines :under-or-before))
(if (fboundp 'tmc-scroll-up)
(def-completion-wrapper tmc-scroll-up :under-or-before))
(if (fboundp 'tmc-scroll-down)
(def-completion-wrapper tmc-scroll-down :under-or-before))
(cmpl-statistics-block
(record-completion-file-loaded))
(provide 'completion)