X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/4586442a5abbb9ccd6e7c4de0730763c0170cc12..b1bad9f3d6bcc725d9727c2dc4282c6080447cbf:/lisp/abbrev.el diff --git a/lisp/abbrev.el b/lisp/abbrev.el index 83a3fbbe49..e8e89e92f4 100644 --- a/lisp/abbrev.el +++ b/lisp/abbrev.el @@ -1,17 +1,17 @@ ;;; abbrev.el --- abbrev mode commands for Emacs ;; Copyright (C) 1985, 1986, 1987, 1992, 2001, 2002, 2003, 2004, -;; 2005, 2006, 2007 Free Software Foundation, Inc. +;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: abbrev convenience ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,16 +19,26 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; This facility is documented in the Emacs Manual. +;; Todo: + +;; - Make abbrev-file-name obey user-emacs-directory. +;; - Cleanup name space. + ;;; Code: +(eval-when-compile (require 'cl)) + +(defgroup abbrev-mode nil + "Word abbreviations mode." + :link '(custom-manual "(emacs)Abbrevs") + :group 'abbrev) + (defcustom only-global-abbrevs nil "Non-nil means user plans to use global abbrevs only. This makes the commands that normally define mode-specific abbrevs @@ -37,16 +47,11 @@ define global abbrevs instead." :group 'abbrev-mode :group 'convenience) -(defun abbrev-mode (&optional arg) +(define-minor-mode abbrev-mode "Toggle Abbrev mode in the current buffer. -With argument ARG, turn abbrev mode on iff ARG is positive. -In Abbrev mode, inserting an abbreviation causes it to expand -and be replaced by its expansion." - (interactive "P") - (setq abbrev-mode - (if (null arg) (not abbrev-mode) - (> (prefix-numeric-value arg) 0))) - (force-mode-line-update)) +With optional argument ARG, turn abbrev mode on if ARG is +positive, otherwise turn it off. In Abbrev mode, inserting an +abbreviation causes it to expand and be replaced by its expansion.") (defcustom abbrev-mode nil "Enable or disable Abbrev mode. @@ -58,7 +63,7 @@ Interactively, use the command `abbrev-mode' to enable or disable Abbrev mode in the current buffer." :type 'boolean :group 'abbrev-mode) -;;;###autoload(put 'abbrev-mode 'safe-local-variable 'booleanp) +(put 'abbrev-mode 'safe-local-variable 'booleanp) (defvar edit-abbrevs-map @@ -71,10 +76,8 @@ to enable or disable Abbrev mode in the current buffer." (defun kill-all-abbrevs () "Undefine all defined abbrevs." (interactive) - (let ((tables abbrev-table-name-list)) - (while tables - (clear-abbrev-table (symbol-value (car tables))) - (setq tables (cdr tables))))) + (dolist (tablesym abbrev-table-name-list) + (clear-abbrev-table (symbol-value tablesym)))) (defun copy-abbrev-table (table) "Make a new abbrev-table with the same abbrevs as TABLE." @@ -94,10 +97,8 @@ Mark is set after the inserted text." (interactive) (push-mark (save-excursion - (let ((tables abbrev-table-name-list)) - (while tables - (insert-abbrev-table-description (car tables) t) - (setq tables (cdr tables)))) + (dolist (tablesym abbrev-table-name-list) + (insert-abbrev-table-description tablesym t)) (point)))) (defun list-abbrevs (&optional local) @@ -119,18 +120,17 @@ Otherwise display all abbrevs." found)) (defun prepare-abbrev-list-buffer (&optional local) - (save-excursion - (let ((table local-abbrev-table)) - (set-buffer (get-buffer-create "*Abbrevs*")) - (erase-buffer) - (if local - (insert-abbrev-table-description (abbrev-table-name table) t) - (dolist (table abbrev-table-name-list) - (insert-abbrev-table-description table t))) - (goto-char (point-min)) - (set-buffer-modified-p nil) - (edit-abbrevs-mode) - (current-buffer)))) + (with-current-buffer (get-buffer-create "*Abbrevs*") + (erase-buffer) + (if local + (insert-abbrev-table-description + (abbrev-table-name local-abbrev-table) t) + (dolist (table abbrev-table-name-list) + (insert-abbrev-table-description table t))) + (goto-char (point-min)) + (set-buffer-modified-p nil) + (edit-abbrevs-mode) + (current-buffer))) (defun edit-abbrevs-mode () "Major mode for editing the list of abbrev definitions. @@ -363,6 +363,586 @@ A prefix argument means don't query; expand all abbrevs." (if (or noquery (y-or-n-p (format "Expand `%s'? " string))) (expand-abbrev))))))) +;;; Abbrev properties. + +(defun abbrev-table-get (table prop) + "Get the PROP property of abbrev table TABLE." + (let ((sym (intern-soft "" table))) + (if sym (get sym prop)))) + +(defun abbrev-table-put (table prop val) + "Set the PROP property of abbrev table TABLE to VAL." + (let ((sym (intern "" table))) + (set sym nil) ; Make sure it won't be confused for an abbrev. + (put sym prop val))) + +(defalias 'abbrev-get 'get + "Get the property PROP of abbrev ABBREV + +\(fn ABBREV PROP)") + +(defalias 'abbrev-put 'put + "Set the property PROP of abbrev ABREV to value VAL. +See `define-abbrev' for the effect of some special properties. + +\(fn ABBREV PROP VAL)") + +(defmacro abbrev-with-wrapper-hook (var &rest body) + "Run BODY wrapped with the VAR hook. +VAR is a special hook: its functions are called with one argument which +is the \"original\" code (the BODY), so the hook function can wrap the +original function, can call it several times, or even not call it at all. +VAR is normally a symbol (a variable) in which case it is treated like a hook, +with a buffer-local and a global part. But it can also be an arbitrary expression. +This is similar to an `around' advice." + (declare (indent 1) (debug t)) + ;; We need those two gensyms because CL's lexical scoping is not available + ;; for function arguments :-( + (let ((funs (make-symbol "funs")) + (global (make-symbol "global"))) + ;; Since the hook is a wrapper, the loop has to be done via + ;; recursion: a given hook function will call its parameter in order to + ;; continue looping. + `(labels ((runrestofhook (,funs ,global) + ;; `funs' holds the functions left on the hook and `global' + ;; holds the functions left on the global part of the hook + ;; (in case the hook is local). + (lexical-let ((funs ,funs) + (global ,global)) + (if (consp funs) + (if (eq t (car funs)) + (runrestofhook (append global (cdr funs)) nil) + (funcall (car funs) + (lambda () (runrestofhook (cdr funs) global)))) + ;; Once there are no more functions on the hook, run + ;; the original body. + ,@body)))) + (runrestofhook ,var + ;; The global part of the hook, if any. + ,(if (symbolp var) + `(if (local-variable-p ',var) + (default-value ',var))))))) + + +;;; Code that used to be implemented in src/abbrev.c + +(defvar abbrev-table-name-list '(fundamental-mode-abbrev-table + global-abbrev-table) + "List of symbols whose values are abbrev tables.") + +(defun make-abbrev-table (&optional props) + "Create a new, empty abbrev table object. +PROPS is a list of properties." + ;; The value 59 is an arbitrary prime number. + (let ((table (make-vector 59 0))) + ;; Each abbrev-table has a `modiff' counter which can be used to detect + ;; when an abbreviation was added. An example of use would be to + ;; construct :regexp dynamically as the union of all abbrev names, so + ;; `modiff' can let us detect that an abbrev was added and hence :regexp + ;; needs to be refreshed. + ;; The presence of `modiff' entry is also used as a tag indicating this + ;; vector is really an abbrev-table. + (abbrev-table-put table :abbrev-table-modiff 0) + (while (consp props) + (abbrev-table-put table (pop props) (pop props))) + table)) + +(defun abbrev-table-p (object) + (and (vectorp object) + (numberp (abbrev-table-get object :abbrev-table-modiff)))) + +(defvar global-abbrev-table (make-abbrev-table) + "The abbrev table whose abbrevs affect all buffers. +Each buffer may also have a local abbrev table. +If it does, the local table overrides the global one +for any particular abbrev defined in both.") + +(defvar abbrev-minor-mode-table-alist nil + "Alist of abbrev tables to use for minor modes. +Each element looks like (VARIABLE . ABBREV-TABLE); +ABBREV-TABLE is active whenever VARIABLE's value is non-nil.") + +(defvar fundamental-mode-abbrev-table + (let ((table (make-abbrev-table))) + ;; Set local-abbrev-table's default to be fundamental-mode-abbrev-table. + (setq-default local-abbrev-table table) + table) + "The abbrev table of mode-specific abbrevs for Fundamental Mode.") + +(defvar abbrevs-changed nil + "Set non-nil by defining or altering any word abbrevs. +This causes `save-some-buffers' to offer to save the abbrevs.") + +(defcustom abbrev-all-caps nil + "Non-nil means expand multi-word abbrevs all caps if abbrev was so." + :type 'boolean + :group 'abbrev-mode) + +(defvar abbrev-start-location nil + "Buffer position for `expand-abbrev' to use as the start of the abbrev. +When nil, use the word before point as the abbrev. +Calling `expand-abbrev' sets this to nil.") + +(defvar abbrev-start-location-buffer nil + "Buffer that `abbrev-start-location' has been set for. +Trying to expand an abbrev in any other buffer clears `abbrev-start-location'.") + +(defvar last-abbrev nil + "The abbrev-symbol of the last abbrev expanded. See `abbrev-symbol'.") + +(defvar last-abbrev-text nil + "The exact text of the last abbrev expanded. +nil if the abbrev has already been unexpanded.") + +(defvar last-abbrev-location 0 + "The location of the start of the last abbrev expanded.") + +;; (defvar local-abbrev-table fundamental-mode-abbrev-table +;; "Local (mode-specific) abbrev table of current buffer.") +;; (make-variable-buffer-local 'local-abbrev-table) + +(defcustom pre-abbrev-expand-hook nil + "Function or functions to be called before abbrev expansion is done. +This is the first thing that `expand-abbrev' does, and so this may change +the current abbrev table before abbrev lookup happens." + :type 'hook + :group 'abbrev-mode) +(make-obsolete-variable 'pre-abbrev-expand-hook 'abbrev-expand-functions "23.1") + +(defun clear-abbrev-table (table) + "Undefine all abbrevs in abbrev table TABLE, leaving it empty." + (setq abbrevs-changed t) + (let* ((sym (intern-soft "" table))) + (dotimes (i (length table)) + (aset table i 0)) + ;; Preserve the table's properties. + (assert sym) + (let ((newsym (intern "" table))) + (set newsym nil) ; Make sure it won't be confused for an abbrev. + (setplist newsym (symbol-plist sym))) + (abbrev-table-put table :abbrev-table-modiff + (1+ (abbrev-table-get table :abbrev-table-modiff))))) + +(defun define-abbrev (table name expansion &optional hook &rest props) + "Define an abbrev in TABLE named NAME, to expand to EXPANSION and call HOOK. +NAME must be a string, and should be lower-case. +EXPANSION should usually be a string. +To undefine an abbrev, define it with EXPANSION = nil. +If HOOK is non-nil, it should be a function of no arguments; +it is called after EXPANSION is inserted. +If EXPANSION is not a string, the abbrev is a special one, + which does not expand in the usual way but only runs HOOK. + +PROPS is a property list. The following properties are special: +- `:count': the value for the abbrev's usage-count, which is incremented each + time the abbrev is used (the default is zero). +- `:system': if non-nil, says that this is a \"system\" abbreviation + which should not be saved in the user's abbreviation file. + Unless `:system' is `force', a system abbreviation will not + overwrite a non-system abbreviation of the same name. +- `:case-fixed': non-nil means that abbreviations are looked up without + case-folding, and the expansion is not capitalized/upcased. +- `:enable-function': a function of no argument which returns non-nil if the + abbrev should be used for a particular call of `expand-abbrev'. + +An obsolete but still supported calling form is: + +\(define-abbrev TABLE NAME EXPANSION &optional HOOK COUNT SYSTEM)." + (when (and (consp props) (or (null (car props)) (numberp (car props)))) + ;; Old-style calling convention. + (setq props (list* :count (car props) + (if (cadr props) (list :system (cadr props)))))) + (unless (plist-get props :count) + (setq props (plist-put props :count 0))) + (let ((system-flag (plist-get props :system)) + (sym (intern name table))) + ;; Don't override a prior user-defined abbrev with a system abbrev, + ;; unless system-flag is `force'. + (unless (and (not (memq system-flag '(nil force))) + (boundp sym) (symbol-value sym) + (not (abbrev-get sym :system))) + (unless (or system-flag + (and (boundp sym) (fboundp sym) + ;; load-file-name + (equal (symbol-value sym) expansion) + (equal (symbol-function sym) hook))) + (setq abbrevs-changed t)) + (set sym expansion) + (fset sym hook) + (setplist sym + ;; Don't store the `force' value of `system-flag' into + ;; the :system property. + (if (eq 'force system-flag) (plist-put props :system t) props)) + (abbrev-table-put table :abbrev-table-modiff + (1+ (abbrev-table-get table :abbrev-table-modiff)))) + name)) + +(defun abbrev--check-chars (abbrev global) + "Check if the characters in ABBREV have word syntax in either the +current (if global is nil) or standard syntax table." + (with-syntax-table + (cond ((null global) (standard-syntax-table)) + ;; ((syntax-table-p global) global) + (t (syntax-table))) + (when (string-match "\\W" abbrev) + (let ((badchars ()) + (pos 0)) + (while (string-match "\\W" abbrev pos) + (pushnew (aref abbrev (match-beginning 0)) badchars) + (setq pos (1+ pos))) + (error "Some abbrev characters (%s) are not word constituents %s" + (apply 'string (nreverse badchars)) + (if global "in the standard syntax" "in this mode")))))) + +(defun define-global-abbrev (abbrev expansion) + "Define ABBREV as a global abbreviation for EXPANSION. +The characters in ABBREV must all be word constituents in the standard +syntax table." + (interactive "sDefine global abbrev: \nsExpansion for %s: ") + (abbrev--check-chars abbrev 'global) + (define-abbrev global-abbrev-table (downcase abbrev) expansion)) + +(defun define-mode-abbrev (abbrev expansion) + "Define ABBREV as a mode-specific abbreviation for EXPANSION. +The characters in ABBREV must all be word-constituents in the current mode." + (interactive "sDefine mode abbrev: \nsExpansion for %s: ") + (unless local-abbrev-table + (error "Major mode has no abbrev table")) + (abbrev--check-chars abbrev nil) + (define-abbrev local-abbrev-table (downcase abbrev) expansion)) + +(defun abbrev--active-tables (&optional tables) + "Return the list of abbrev tables currently active. +TABLES if non-nil overrides the usual rules. It can hold +either a single abbrev table or a list of abbrev tables." + ;; We could just remove the `tables' arg and let callers use + ;; (or table (abbrev--active-tables)) but then they'd have to be careful + ;; to treat the distinction between a single table and a list of tables. + (cond + ((consp tables) tables) + ((vectorp tables) (list tables)) + (t + (let ((tables (if (listp local-abbrev-table) + (append local-abbrev-table + (list global-abbrev-table)) + (list local-abbrev-table global-abbrev-table)))) + ;; Add the minor-mode abbrev tables. + (dolist (x abbrev-minor-mode-table-alist) + (when (and (symbolp (car x)) (boundp (car x)) (symbol-value (car x))) + (setq tables + (if (listp (cdr x)) + (append (cdr x) tables) (cons (cdr x) tables))))) + tables)))) + + +(defun abbrev-symbol (abbrev &optional table) + "Return the symbol representing abbrev named ABBREV. +This symbol's name is ABBREV, but it is not the canonical symbol of that name; +it is interned in an abbrev-table rather than the normal obarray. +The value is nil if that abbrev is not defined. +Optional second arg TABLE is abbrev table to look it up in. +The default is to try buffer's mode-specific abbrev table, then global table." + (let ((tables (abbrev--active-tables table)) + sym) + (while (and tables (not (symbol-value sym))) + (let* ((table (pop tables)) + (case-fold (not (abbrev-table-get table :case-fixed)))) + (setq tables (append (abbrev-table-get table :parents) tables)) + ;; In case the table doesn't set :case-fixed but some of the + ;; abbrevs do, we have to be careful. + (setq sym + ;; First try without case-folding. + (or (intern-soft abbrev table) + (when case-fold + ;; We didn't find any abbrev, try case-folding. + (let ((sym (intern-soft (downcase abbrev) table))) + ;; Only use it if it doesn't require :case-fixed. + (and sym (not (abbrev-get sym :case-fixed)) + sym))))))) + (if (symbol-value sym) + sym))) + + +(defun abbrev-expansion (abbrev &optional table) + "Return the string that ABBREV expands into in the current buffer. +Optionally specify an abbrev table as second arg; +then ABBREV is looked up in that table only." + (symbol-value (abbrev-symbol abbrev table))) + + +(defun abbrev--before-point () + "Try and find an abbrev before point. Return it if found, nil otherwise." + (unless (eq abbrev-start-location-buffer (current-buffer)) + (setq abbrev-start-location nil)) + + (let ((tables (abbrev--active-tables)) + (pos (point)) + start end name res) + + (if abbrev-start-location + (progn + (setq start abbrev-start-location) + (setq abbrev-start-location nil) + ;; Remove the hyphen inserted by `abbrev-prefix-mark'. + (if (and (< start (point-max)) + (eq (char-after start) ?-)) + (delete-region start (1+ start))) + (skip-syntax-backward " ") + (setq end (point)) + (when (> end start) + (setq name (buffer-substring start end)) + (goto-char pos) ; Restore point. + (list (abbrev-symbol name tables) name start end))) + + (while (and tables (not (car res))) + (let* ((table (pop tables)) + (enable-fun (abbrev-table-get table :enable-function))) + (setq tables (append (abbrev-table-get table :parents) tables)) + (setq res + (and (or (not enable-fun) (funcall enable-fun)) + (looking-back (or (abbrev-table-get table :regexp) + "\\<\\(\\w+\\)\\W*") + (line-beginning-position)) + (setq start (match-beginning 1)) + (setq end (match-end 1)) + (setq name (buffer-substring start end)) + (let ((abbrev (abbrev-symbol name table))) + (when abbrev + (setq enable-fun (abbrev-get abbrev :enable-function)) + (and (or (not enable-fun) (funcall enable-fun)) + ;; This will also look it up in parent tables. + ;; This is not on purpose, but it seems harmless. + (list abbrev name start end)))))) + ;; Restore point. + (goto-char pos))) + res))) + +(defun abbrev-insert (abbrev &optional name wordstart wordend) + "Insert abbrev ABBREV at point. +If non-nil, NAME is the name by which this abbrev was found. +If non-nil, WORDSTART is the place where to insert the abbrev. +If non-nil, WORDEND the abbrev replaces the previous text between +WORDSTART and WORDEND. +Return ABBREV if the expansion should be considered as having taken place." + (unless name (setq name (symbol-name abbrev))) + (unless wordstart (setq wordstart (point))) + (unless wordend (setq wordend wordstart)) + ;; Increment use count. + (abbrev-put abbrev :count (1+ (abbrev-get abbrev :count))) + (let ((value abbrev)) + ;; If this abbrev has an expansion, delete the abbrev + ;; and insert the expansion. + (when (stringp (symbol-value abbrev)) + (goto-char wordstart) + ;; Insert at beginning so that markers at the end (e.g. point) + ;; are preserved. + (insert (symbol-value abbrev)) + (delete-char (- wordend wordstart)) + (let ((case-fold-search nil)) + ;; If the abbrev's name is different from the buffer text (the + ;; only difference should be capitalization), then we may want + ;; to adjust the capitalization of the expansion. + (when (and (not (equal name (symbol-name abbrev))) + (string-match "[[:upper:]]" name)) + (if (not (string-match "[[:lower:]]" name)) + ;; Abbrev was all caps. If expansion is multiple words, + ;; normally capitalize each word. + (if (and (not abbrev-all-caps) + (save-excursion + (> (progn (backward-word 1) (point)) + (progn (goto-char wordstart) + (forward-word 1) (point))))) + (upcase-initials-region wordstart (point)) + (upcase-region wordstart (point))) + ;; Abbrev included some caps. Cap first initial of expansion. + (let ((end (point))) + ;; Find the initial. + (goto-char wordstart) + (skip-syntax-forward "^w" (1- end)) + ;; Change just that. + (upcase-initials-region (point) (1+ (point))) + (goto-char end)))))) + ;; Now point is at the end of the expansion and the beginning is + ;; in last-abbrev-location. + (when (symbol-function abbrev) + (let* ((hook (symbol-function abbrev)) + (expanded + ;; If the abbrev has a hook function, run it. + (funcall hook))) + ;; In addition, if the hook function is a symbol with + ;; a non-nil `no-self-insert' property, let the value it + ;; returned specify whether we consider that an expansion took + ;; place. If it returns nil, no expansion has been done. + (if (and (symbolp hook) + (null expanded) + (get hook 'no-self-insert)) + (setq value nil)))) + value)) + +(defvar abbrev-expand-functions nil + "Wrapper hook around `expand-abbrev'. +The functions on this special hook are called with one argument: +a function that performs the abbrev expansion. It should return +the abbrev symbol if expansion took place.") + +(defun expand-abbrev () + "Expand the abbrev before point, if there is an abbrev there. +Effective when explicitly called even when `abbrev-mode' is nil. +Returns the abbrev symbol, if expansion took place." + (interactive) + (run-hooks 'pre-abbrev-expand-hook) + (abbrev-with-wrapper-hook abbrev-expand-functions + (destructuring-bind (&optional sym name wordstart wordend) + (abbrev--before-point) + (when sym + (let ((value sym)) + (unless (or ;; executing-kbd-macro + noninteractive + (window-minibuffer-p (selected-window))) + ;; Add an undo boundary, in case we are doing this for + ;; a self-inserting command which has avoided making one so far. + (undo-boundary)) + ;; Now sym is the abbrev symbol. + (setq last-abbrev-text name) + (setq last-abbrev sym) + (setq last-abbrev-location wordstart) + ;; If this abbrev has an expansion, delete the abbrev + ;; and insert the expansion. + (abbrev-insert sym name wordstart wordend)))))) + +(defun unexpand-abbrev () + "Undo the expansion of the last abbrev that expanded. +This differs from ordinary undo in that other editing done since then +is not undone." + (interactive) + (save-excursion + (unless (or (< last-abbrev-location (point-min)) + (> last-abbrev-location (point-max))) + (goto-char last-abbrev-location) + (when (stringp last-abbrev-text) + ;; This isn't correct if last-abbrev's hook was used + ;; to do the expansion. + (let ((val (symbol-value last-abbrev))) + (unless (stringp val) + (error "Value of abbrev-symbol must be a string")) + ;; Don't inherit properties here; just copy from old contents. + (insert last-abbrev-text) + ;; Delete after inserting, to better preserve markers. + (delete-region (point) (+ (point) (length val))) + (setq last-abbrev-text nil)))))) + +(defun abbrev--write (sym) + "Write the abbrev in a `read'able form. +Only writes the non-system abbrevs. +Presumes that `standard-output' points to `current-buffer'." + (unless (or (null (symbol-value sym)) (abbrev-get sym :system)) + (insert " (") + (prin1 (symbol-name sym)) + (insert " ") + (prin1 (symbol-value sym)) + (insert " ") + (prin1 (symbol-function sym)) + (insert " ") + (prin1 (abbrev-get sym :count)) + (insert ")\n"))) + +(defun abbrev--describe (sym) + (when (symbol-value sym) + (prin1 (symbol-name sym)) + (if (null (abbrev-get sym :system)) + (indent-to 15 1) + (insert " (sys)") + (indent-to 20 1)) + (prin1 (abbrev-get sym :count)) + (indent-to 20 1) + (prin1 (symbol-value sym)) + (when (symbol-function sym) + (indent-to 45 1) + (prin1 (symbol-function sym))) + (terpri))) + +(defun insert-abbrev-table-description (name &optional readable) + "Insert before point a full description of abbrev table named NAME. +NAME is a symbol whose value is an abbrev table. +If optional 2nd arg READABLE is non-nil, a human-readable description +is inserted. Otherwise the description is an expression, +a call to `define-abbrev-table', which would +define the abbrev table NAME exactly as it is currently defined. + +Abbrevs marked as \"system abbrevs\" are omitted." + (let ((table (symbol-value name)) + (symbols ())) + (mapatoms (lambda (sym) (if (symbol-value sym) (push sym symbols))) table) + (setq symbols (sort symbols 'string-lessp)) + (let ((standard-output (current-buffer))) + (if readable + (progn + (insert "(") + (prin1 name) + (insert ")\n\n") + (mapc 'abbrev--describe symbols) + (insert "\n\n")) + (insert "(define-abbrev-table '") + (prin1 name) + (insert " '(") + (mapc 'abbrev--write symbols) + (insert " ))\n\n")) + nil))) + +(put 'define-abbrev-table 'doc-string-elt 3) +(defun define-abbrev-table (tablename definitions + &optional docstring &rest props) + "Define TABLENAME (a symbol) as an abbrev table name. +Define abbrevs in it according to DEFINITIONS, which is a list of elements +of the form (ABBREVNAME EXPANSION ...) that are passed to `define-abbrev'. +PROPS is a property list to apply to the table. +Properties with special meaning: +- `:parents' contains a list of abbrev tables from which this table inherits + abbreviations. +- `:case-fixed' non-nil means that abbreviations are looked up without + case-folding, and the expansion is not capitalized/upcased. +- `:regexp' describes the form of abbrevs. It defaults to \\=\\<\\(\\w+\\)\\W* which + means that an abbrev can only be a single word. The submatch 1 is treated + as the potential name of an abbrev. +- `:enable-function' can be set to a function of no argument which returns + non-nil if and only if the abbrevs in this table should be used for this + instance of `expand-abbrev'." + ;; We used to manually add the docstring, but we also want to record this + ;; location as the definition of the variable (in load-history), so we may + ;; as well just use `defvar'. + (eval `(defvar ,tablename nil ,@(if (stringp docstring) (list docstring)))) + (let ((table (if (boundp tablename) (symbol-value tablename)))) + (unless table + (setq table (make-abbrev-table)) + (set tablename table) + (push tablename abbrev-table-name-list)) + ;; We used to just pass them to `make-abbrev-table', but that fails + ;; if the table was pre-existing as is the case if it was created by + ;; loading the user's abbrev file. + (while (consp props) + (abbrev-table-put table (pop props) (pop props))) + (dolist (elt definitions) + (apply 'define-abbrev table elt)))) + +(defun abbrev-table-menu (table &optional prompt sortfun) + "Return a menu that shows all abbrevs in TABLE. +Selecting an entry runs `abbrev-insert'. +PROMPT is the prompt to use for the keymap. +SORTFUN is passed to `sort' to change the default ordering." + (unless sortfun (setq sortfun 'string-lessp)) + (let ((entries ())) + (mapatoms (lambda (abbrev) + (when (symbol-value abbrev) + (let ((name (symbol-name abbrev))) + (push `(,(intern name) menu-item ,name + (lambda () (interactive) + (abbrev-insert ',abbrev))) + entries)))) + table) + (nconc (make-sparse-keymap prompt) + (sort entries (lambda (x y) + (funcall sortfun (nth 2 x) (nth 2 y))))))) + (provide 'abbrev) ;; arch-tag: dbd6f3ae-dfe3-40ba-b00f-f9e3ff960df5