| 1 | ;;; expand.el --- make abbreviations more usable |
| 2 | |
| 3 | ;; Copyright (C) 1995-1996, 2001-2014 Free Software Foundation, Inc. |
| 4 | |
| 5 | ;; Author: Frederic Lepied <Frederic.Lepied@sugix.frmug.org> |
| 6 | ;; Maintainer: Frederic Lepied <Frederic.Lepied@sugix.frmug.org> |
| 7 | ;; Keywords: abbrev |
| 8 | |
| 9 | ;; This file is part of GNU Emacs. |
| 10 | |
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
| 12 | ;; it under the terms of the GNU General Public License as published by |
| 13 | ;; the Free Software Foundation, either version 3 of the License, or |
| 14 | ;; (at your option) any later version. |
| 15 | |
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | ;; GNU General Public License for more details. |
| 20 | |
| 21 | ;; You should have received a copy of the GNU General Public License |
| 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
| 23 | |
| 24 | ;;; Commentary: |
| 25 | ;; |
| 26 | ;; This package defines abbrevs which expand into structured constructs |
| 27 | ;; for certain languages. The construct is indented for you, |
| 28 | ;; and contains slots for you to fill in other text. |
| 29 | |
| 30 | ;; These abbrevs expand only at the end of a line and when not in a comment |
| 31 | ;; or a string. |
| 32 | ;; |
| 33 | ;; Look at the Sample: section for emacs-lisp, perl and c expand lists. |
| 34 | ;; For example for c-mode, you could declare your abbrev table with : |
| 35 | ;; |
| 36 | ;; (defconst c-expand-list |
| 37 | ;; '(("if" "if () {\n \n} else {\n \n}" (5 10 21)) |
| 38 | ;; ("ifn" "if () {}" (5 8)) |
| 39 | ;; ("uns" "unsigned ") |
| 40 | ;; ("for" "for(; ; ) {\n\n}" (5 7 9 13)) |
| 41 | ;; ("switch" "switch () {\n\n}" (9 13)) |
| 42 | ;; ("case" "case :\n\nbreak;\n" (6 8 16)) |
| 43 | ;; ("do" "do {\n\n} while ();" (6 16)) |
| 44 | ;; ("while" "while () {\n\n}" (8 12)) |
| 45 | ;; ("default" "default:\n\nbreak;" 10) |
| 46 | ;; ("main" "int\nmain(int argc, char * argv[])\n{\n\n}\n" 37)) |
| 47 | ;; "Expansions for C mode") |
| 48 | ;; |
| 49 | ;; and enter Abbrev mode with the following hook : |
| 50 | ;; |
| 51 | ;; (add-hook 'c-mode-hook |
| 52 | ;; (lambda () |
| 53 | ;; (expand-add-abbrevs c-mode-abbrev-table c-expand-list) |
| 54 | ;; (abbrev-mode 1))) |
| 55 | ;; |
| 56 | ;; you can also init some post-process hooks : |
| 57 | ;; |
| 58 | ;; (add-hook 'expand-load-hook |
| 59 | ;; (lambda () |
| 60 | ;; (add-hook 'expand-expand-hook 'indent-according-to-mode) |
| 61 | ;; (add-hook 'expand-jump-hook 'indent-according-to-mode))) |
| 62 | ;; |
| 63 | ;; Remarks: |
| 64 | ;; |
| 65 | ;; Many thanks to Heddy Boubaker <boubaker@cenatls.cena.dgac.fr>, |
| 66 | ;; Jerome Santini <santini@chambord.univ-orleans.fr>, |
| 67 | ;; Jari Aalto <jaalto@tre.tele.nokia.fi>. |
| 68 | ;; |
| 69 | ;; Please send me a word to give me your feeling about this feature or |
| 70 | ;; to explain me how you use it (your expansions table for example) using |
| 71 | ;; the function expand-submit-report. |
| 72 | ;;; Code: |
| 73 | \f |
| 74 | ;;; Constants: |
| 75 | |
| 76 | (defgroup expand nil |
| 77 | "Make abbreviations more usable." |
| 78 | :group 'abbrev) |
| 79 | |
| 80 | (defcustom expand-load-hook nil |
| 81 | "Hooks run when `expand.el' is loaded." |
| 82 | :type 'hook |
| 83 | :group 'expand) |
| 84 | |
| 85 | (defcustom expand-expand-hook nil |
| 86 | "Hooks run when an abbrev made by `expand-add-abbrevs' is expanded." |
| 87 | :type 'hook |
| 88 | :group 'expand) |
| 89 | |
| 90 | (defcustom expand-jump-hook nil |
| 91 | "Hooks run by `expand-jump-to-previous-slot' and `expand-jump-to-next-slot'." |
| 92 | :type 'hook |
| 93 | :group 'expand) |
| 94 | |
| 95 | ;;; Samples: |
| 96 | |
| 97 | (define-skeleton expand-c-for-skeleton "For loop skeleton" |
| 98 | "Loop var: " |
| 99 | "for(" str _ @ "=0; " str @ "; " str @ ") {" \n |
| 100 | @ _ \n |
| 101 | "}" > \n) |
| 102 | |
| 103 | (defconst expand-c-sample-expand-list |
| 104 | '(("if" "if () {\n \n} else {\n \n}" (5 10 21)) |
| 105 | ("ifn" "if () {}" (5 8)) |
| 106 | ("uns" "unsigned ") |
| 107 | ("for" expand-c-for-skeleton) |
| 108 | ("switch" "switch () {\n\n}" (9 13)) |
| 109 | ("case" "case :\n\nbreak;\n" (6 8 16)) |
| 110 | ("do" "do {\n\n} while ();" (6 16)) |
| 111 | ("while" "while () {\n\n}" (8 12)) |
| 112 | ("default" "default:\n\nbreak;" 10) |
| 113 | ("main" "int\nmain(int argc, char * argv[])\n{\n\n}\n" 37)) |
| 114 | "Expansions for C mode. See `expand-add-abbrevs'.") |
| 115 | |
| 116 | ;; lisp example from Jari Aalto <jaalto@tre.tele.nokia.fi> |
| 117 | (defconst expand-sample-lisp-mode-expand-list |
| 118 | (list |
| 119 | (list |
| 120 | "defu" |
| 121 | (concat |
| 122 | "(defun ()\n" |
| 123 | " \"\"\n" |
| 124 | " (interactive)\n" |
| 125 | " (let* (\n" |
| 126 | " )\n" |
| 127 | " \n" |
| 128 | " ))") |
| 129 | (list 8 11 16 32 43 59)) |
| 130 | |
| 131 | (list |
| 132 | "defs" |
| 133 | (concat |
| 134 | "(defsubst ()\n" |
| 135 | " \"\"\n" |
| 136 | " (interactive)\n" |
| 137 | " )") |
| 138 | (list 11 14 19 23 39)) |
| 139 | |
| 140 | (list |
| 141 | "defm" |
| 142 | (concat |
| 143 | "(defmacro ()\n" |
| 144 | " \"\"\n" |
| 145 | " `( \n" |
| 146 | " ))") |
| 147 | (list 11 13 18 25)) |
| 148 | |
| 149 | (list |
| 150 | "defa" |
| 151 | (concat |
| 152 | "(defadvice (around act)\n" |
| 153 | " \"\"\n" |
| 154 | " \n" |
| 155 | " )") |
| 156 | (list 12 22 32 36)) |
| 157 | |
| 158 | (list |
| 159 | "defc" |
| 160 | "(defconst nil\n \"\")\n" |
| 161 | (list 11 13 20)) |
| 162 | |
| 163 | (list |
| 164 | "defv" |
| 165 | "(defvar nil\n \"\")\n" |
| 166 | (list 9 11 18)) |
| 167 | |
| 168 | (list |
| 169 | "let" |
| 170 | "(let* (\n)\n " |
| 171 | (list 8 13)) |
| 172 | |
| 173 | (list |
| 174 | "sav" |
| 175 | "(save-excursion\n \n)" |
| 176 | (list 18)) |
| 177 | |
| 178 | (list |
| 179 | "aut" |
| 180 | "(autoload ' \"\" t t)\n" |
| 181 | (list 12 14)) |
| 182 | |
| 183 | ) |
| 184 | "Expansions for Lisp mode. See `expand-add-abbrevs'.") |
| 185 | |
| 186 | ;; perl example from Jari Aalto <jaalto@tre.tele.nokia.fi> |
| 187 | (defconst expand-sample-perl-mode-expand-list |
| 188 | (list |
| 189 | (list |
| 190 | ;; This is default perl4 subroutine template |
| 191 | ;; |
| 192 | "sub" |
| 193 | (concat |
| 194 | "#" (make-string 70 ?-) "\n" |
| 195 | "sub {\n" |
| 196 | " # DESCRIPTION\n" |
| 197 | " # \n" |
| 198 | " # \n" |
| 199 | " # INPUT\n" |
| 200 | " # \n" |
| 201 | " # \n" |
| 202 | " # RETURN\n" |
| 203 | " # \n" |
| 204 | "\n" |
| 205 | " local( $f ) = \"$lib.\";\n" ;; Function name AFTER period |
| 206 | " local() = @_;\n" ;; func arguments here |
| 207 | " \n" |
| 208 | " \n}\n" |
| 209 | ) |
| 210 | (list 77 88 120 146 159 176)) |
| 211 | |
| 212 | (list |
| 213 | "for" ; foreach |
| 214 | (concat |
| 215 | "for ( )\n" |
| 216 | "{\n\n\}" |
| 217 | ) |
| 218 | (list 7 12)) |
| 219 | |
| 220 | (list |
| 221 | "whi" ; foreach |
| 222 | (concat |
| 223 | "while ( )\n" |
| 224 | "{\n\n\}" |
| 225 | ) |
| 226 | (list 9 15)) |
| 227 | |
| 228 | |
| 229 | ;; The normal "if" can be used like |
| 230 | ;; print $F "xxxxxx" if defined @arr; |
| 231 | ;; |
| 232 | (list |
| 233 | "iff" |
| 234 | (concat |
| 235 | "if ( )\n" |
| 236 | "{\n\n\}" |
| 237 | ) |
| 238 | (list 6 12)) |
| 239 | |
| 240 | (list "loc" "local( $ );" (list 9)) |
| 241 | (list "my" "my( $ );" (list 6)) |
| 242 | (list "ope" "open(,\"\")\t|| die \"$f: Can't open [$]\";" (list 6 8 36)) |
| 243 | (list "clo" "close ;" 7) |
| 244 | (list "def" "defined " (list 9)) |
| 245 | (list "und" "undef ;" (list 7)) |
| 246 | |
| 247 | ;; There is no ending colon, because they can be in statement |
| 248 | ;; defined $REXP_NOT_NEW && (print "xxxxx" ); |
| 249 | ;; |
| 250 | (list "pr" "print " 7) |
| 251 | (list "pf" "printf " 8) |
| 252 | |
| 253 | |
| 254 | (list "gre" "grep( //, );" (list 8 11)) |
| 255 | (list "pus" "push( , );" (list 7 9)) |
| 256 | (list "joi" "join( '', );" (list 7 11)) |
| 257 | (list "rtu" "return ;" (list 8)) |
| 258 | |
| 259 | ) |
| 260 | "Expansions for Perl mode. See `expand-add-abbrevs'.") |
| 261 | |
| 262 | ;;; Code: |
| 263 | |
| 264 | ;;;###autoload |
| 265 | (defun expand-add-abbrevs (table abbrevs) |
| 266 | "Add a list of abbreviations to abbrev table TABLE. |
| 267 | ABBREVS is a list of abbrev definitions; each abbrev description entry |
| 268 | has the form (ABBREV EXPANSION ARG). |
| 269 | |
| 270 | ABBREV is the abbreviation to replace. |
| 271 | |
| 272 | EXPANSION is the replacement string or a function which will make the |
| 273 | expansion. For example, you could use the DMacros or skeleton packages |
| 274 | to generate such functions. |
| 275 | |
| 276 | ARG is an optional argument which can be a number or a list of |
| 277 | numbers. If ARG is a number, point is placed ARG chars from the |
| 278 | beginning of the expanded text. |
| 279 | |
| 280 | If ARG is a list of numbers, point is placed according to the first |
| 281 | member of the list, but you can visit the other specified positions |
| 282 | cyclically with the functions `expand-jump-to-previous-slot' and |
| 283 | `expand-jump-to-next-slot'. |
| 284 | |
| 285 | If ARG is omitted, point is placed at the end of the expanded text." |
| 286 | |
| 287 | (if (null abbrevs) |
| 288 | table |
| 289 | (expand-add-abbrev table (nth 0 (car abbrevs)) (nth 1 (car abbrevs)) |
| 290 | (nth 2 (car abbrevs))) |
| 291 | (expand-add-abbrevs table (cdr abbrevs)))) |
| 292 | |
| 293 | (defvar expand-list nil "Temporary variable used by the Expand package.") |
| 294 | |
| 295 | (defvar expand-pos nil |
| 296 | "If non-nil, stores a vector containing markers to positions defined by the last expansion.") |
| 297 | (make-variable-buffer-local 'expand-pos) |
| 298 | |
| 299 | (defvar expand-index 0 |
| 300 | "Index of the last marker used in `expand-pos'.") |
| 301 | (make-variable-buffer-local 'expand-index) |
| 302 | |
| 303 | (defvar expand-point nil |
| 304 | "End of the expanded region.") |
| 305 | (make-variable-buffer-local 'expand-point) |
| 306 | |
| 307 | (defun expand-add-abbrev (table abbrev expansion arg) |
| 308 | "Add one abbreviation and provide the hook to move to the specified positions." |
| 309 | (let* ((string-exp (if (and (symbolp expansion) (fboundp expansion)) |
| 310 | nil |
| 311 | expansion)) |
| 312 | (position (if (and arg string-exp) |
| 313 | (if (listp arg) |
| 314 | (- (length expansion) (1- (car arg))) |
| 315 | (- (length expansion) (1- arg))) |
| 316 | 0))) |
| 317 | (define-abbrev |
| 318 | table |
| 319 | abbrev |
| 320 | (vector string-exp |
| 321 | position |
| 322 | (if (and (listp arg) |
| 323 | (not (null arg))) |
| 324 | (cons (length string-exp) arg) |
| 325 | nil) |
| 326 | (if (and (symbolp expansion) (fboundp expansion)) |
| 327 | expansion |
| 328 | nil) |
| 329 | ) |
| 330 | 'expand-abbrev-hook))) |
| 331 | |
| 332 | (put 'expand-abbrev-hook 'no-self-insert t) |
| 333 | ;;;###autoload |
| 334 | (defun expand-abbrev-hook () |
| 335 | "Abbrev hook used to do the expansion job of expand abbrevs. |
| 336 | See `expand-add-abbrevs'. Value is non-nil if expansion was done." |
| 337 | ;; Expand only at the end of a line if we are near a word that has |
| 338 | ;; an abbrev built from expand-add-abbrev. |
| 339 | (if (and (eolp) |
| 340 | (not (expand-in-literal))) |
| 341 | (let ((p (point))) |
| 342 | (setq expand-point nil) |
| 343 | ;; don't expand if the preceding char isn't a word constituent |
| 344 | (if (and (eq (char-syntax (preceding-char)) |
| 345 | ?w) |
| 346 | (expand-do-expansion)) |
| 347 | (progn |
| 348 | ;; expand-point tells us if we have inserted the text |
| 349 | ;; ourself or if it is the hook which has done the job. |
| 350 | (if expand-point |
| 351 | (progn |
| 352 | (if (vectorp expand-list) |
| 353 | (expand-build-marks expand-point)) |
| 354 | (indent-region p expand-point nil)) |
| 355 | ;; an outside function can set expand-list to a list of |
| 356 | ;; markers in reverse order. |
| 357 | (if (listp expand-list) |
| 358 | (setq expand-index 0 |
| 359 | expand-pos (expand-list-to-markers expand-list) |
| 360 | expand-list nil))) |
| 361 | (run-hooks 'expand-expand-hook) |
| 362 | t) |
| 363 | nil)) |
| 364 | nil)) |
| 365 | |
| 366 | (defun expand-do-expansion () |
| 367 | (delete-char (- (length last-abbrev-text))) |
| 368 | (let* ((vect (symbol-value last-abbrev)) |
| 369 | (text (aref vect 0)) |
| 370 | (position (aref vect 1)) |
| 371 | (jump-args (aref vect 2)) |
| 372 | (hook (aref vect 3))) |
| 373 | (cond (text |
| 374 | (insert text) |
| 375 | (setq expand-point (point)))) |
| 376 | (if jump-args |
| 377 | (funcall 'expand-build-list (car jump-args) (cdr jump-args))) |
| 378 | (if position |
| 379 | (backward-char position)) |
| 380 | (if hook |
| 381 | (funcall hook)) |
| 382 | t) |
| 383 | ) |
| 384 | |
| 385 | (defun expand-abbrev-from-expand (word) |
| 386 | "Test if an abbrev has a hook." |
| 387 | (or |
| 388 | (and (intern-soft word local-abbrev-table) |
| 389 | (symbol-function (intern-soft word local-abbrev-table))) |
| 390 | (and (intern-soft word global-abbrev-table) |
| 391 | (symbol-function (intern-soft word global-abbrev-table))))) |
| 392 | |
| 393 | (defun expand-previous-word () |
| 394 | "Return the previous word." |
| 395 | (save-excursion |
| 396 | (let ((p (point))) |
| 397 | (backward-word 1) |
| 398 | (buffer-substring p (point))))) |
| 399 | |
| 400 | ;;;###autoload |
| 401 | (defun expand-jump-to-previous-slot () |
| 402 | "Move the cursor to the previous slot in the last abbrev expansion. |
| 403 | This is used only in conjunction with `expand-add-abbrevs'." |
| 404 | (interactive) |
| 405 | (if expand-pos |
| 406 | (progn |
| 407 | (setq expand-index (1- expand-index)) |
| 408 | (if (< expand-index 0) |
| 409 | (setq expand-index (1- (length expand-pos)))) |
| 410 | (goto-char (aref expand-pos expand-index)) |
| 411 | (run-hooks 'expand-jump-hook)))) |
| 412 | |
| 413 | ;;;###autoload |
| 414 | (defun expand-jump-to-next-slot () |
| 415 | "Move the cursor to the next slot in the last abbrev expansion. |
| 416 | This is used only in conjunction with `expand-add-abbrevs'." |
| 417 | (interactive) |
| 418 | (if expand-pos |
| 419 | (progn |
| 420 | (setq expand-index (1+ expand-index)) |
| 421 | (if (>= expand-index (length expand-pos)) |
| 422 | (setq expand-index 0)) |
| 423 | (goto-char (aref expand-pos expand-index)) |
| 424 | (run-hooks 'expand-jump-hook)))) |
| 425 | |
| 426 | ;;;###autoload (define-key abbrev-map "p" 'expand-jump-to-previous-slot) |
| 427 | ;;;###autoload (define-key abbrev-map "n" 'expand-jump-to-next-slot) |
| 428 | |
| 429 | (defun expand-build-list (len l) |
| 430 | "Build a vector of offset positions from the list of positions." |
| 431 | (expand-clear-markers) |
| 432 | (setq expand-list (vconcat l)) |
| 433 | (let ((i 0) |
| 434 | (lenlist (length expand-list))) |
| 435 | (while (< i lenlist) |
| 436 | (aset expand-list i (- len (1- (aref expand-list i)))) |
| 437 | (setq i (1+ i)))) |
| 438 | ) |
| 439 | |
| 440 | (defun expand-build-marks (p) |
| 441 | "Transform the offsets vector into a marker vector." |
| 442 | (if expand-list |
| 443 | (progn |
| 444 | (setq expand-index 0) |
| 445 | (setq expand-pos (make-vector (length expand-list) nil)) |
| 446 | (let ((i (1- (length expand-list)))) |
| 447 | (while (>= i 0) |
| 448 | (aset expand-pos i (copy-marker (- p (aref expand-list i)))) |
| 449 | (setq i (1- i)))) |
| 450 | (setq expand-list nil)))) |
| 451 | |
| 452 | (defun expand-clear-markers () |
| 453 | "Make the markers point nowhere." |
| 454 | (if expand-pos |
| 455 | (progn |
| 456 | (let ((i (1- (length expand-pos)))) |
| 457 | (while (>= i 0) |
| 458 | (set-marker (aref expand-pos i) nil) |
| 459 | (setq i (1- i)))) |
| 460 | (setq expand-pos nil)))) |
| 461 | |
| 462 | (defun expand-in-literal () |
| 463 | "Test if we are in a comment or in a string." |
| 464 | (save-excursion |
| 465 | (let* ((lim (or (save-excursion |
| 466 | (beginning-of-defun) |
| 467 | (point)) |
| 468 | (point-min))) |
| 469 | (state (parse-partial-sexp lim (point)))) |
| 470 | (cond |
| 471 | ((nth 3 state) 'string) |
| 472 | ((nth 4 state) 'comment) |
| 473 | (t nil))))) |
| 474 | |
| 475 | ;; support functions to add marks to jump from outside function |
| 476 | |
| 477 | (defun expand-list-to-markers (l) |
| 478 | "Transform a list of markers in reverse order into a vector in the correct order." |
| 479 | (let* ((len (1- (length l))) |
| 480 | (loop len) |
| 481 | (v (make-vector (+ len 1) nil))) |
| 482 | (while (>= loop 0) |
| 483 | (aset v loop (if (markerp (car l)) (car l) (copy-marker (car l)))) |
| 484 | (setq l (cdr l) |
| 485 | loop (1- loop))) |
| 486 | v)) |
| 487 | |
| 488 | ;; integration with skeleton.el |
| 489 | ;; Used in `skeleton-end-hook' to fetch the positions for @ skeleton tags. |
| 490 | ;; See `skeleton-insert'. |
| 491 | (defun expand-skeleton-end-hook () |
| 492 | (if skeleton-positions |
| 493 | (setq expand-list skeleton-positions))) |
| 494 | |
| 495 | (add-hook 'skeleton-end-hook (function expand-skeleton-end-hook)) |
| 496 | |
| 497 | (provide 'expand) |
| 498 | |
| 499 | ;; run load hooks |
| 500 | (run-hooks 'expand-load-hook) |
| 501 | |
| 502 | ;;; expand.el ends here |