1;;; meta-mode.el --- major mode for editing Metafont or MetaPost sources 2 3;; Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006, 2007 4;; Free Software Foundation, Inc. 5 6;; Author: Ulrik Vieth <vieth@thphy.uni-duesseldorf.de> 7;; Version: 1.0 8;; Keywords: Metafont, MetaPost, tex, languages 9 10;; This file is part of GNU Emacs. 11 12;; GNU Emacs is free software; you can redistribute it and/or modify 13;; it under the terms of the GNU General Public License as published by 14;; the Free Software Foundation; either version 2, or (at your option) 15;; any later version. 16 17;; GNU Emacs is distributed in the hope that it will be useful, 18;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20;; GNU General Public License for more details. 21 22;; You should have received a copy of the GNU General Public License 23;; along with GNU Emacs; see the file COPYING. If not, write to the 24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25;; Boston, MA 02110-1301, USA. 26 27;;; Commentary: 28 29;; Description: 30;; 31;; This Emacs Lisp package provides a major mode for editing Metafont 32;; or MetaPost sources. It includes all the necessary code to set up 33;; a major mode including an approriate syntax table, keymap, and a 34;; mode-specific pull-down menu. It also provides a sophisticated set 35;; of font-lock patterns, a fancy indentation function adapted from 36;; AUCTeX's latex.el, and some basic mode-specific editing functions 37;; such as functions to move to the beginning or end of the enclosing 38;; environment, or to mark, re-indent, or comment-out environments. 39;; On the other hand, it doesn't yet provide any functionality for 40;; running Metafont or MetaPost in a shell buffer form within Emacs, 41;; but such functionality might be added later, either as part of this 42;; package or as a separate Emacs Lisp package. 43 44;; Installation: 45;; 46;; An interface to running Metafont or MetaPost as a shell process 47;; from within Emacs is currently under development as a separate 48;; Emacs Lisp package (meta-buf.el). In order to have that package 49;; loaded automatically when first entering Metafont or MetaPost mode, 50;; you might use the load-hook provided in this package by adding 51;; these lines to your startup file: 52;; 53;; (add-hook 'meta-mode-load-hook 54;; '(lambda () (require 'meta-buf))) 55;; 56;; The add-on package loaded this way may in turn make use of the 57;; mode-hooks provided in this package to activate additional features 58;; when entering Metafont or MetaPost mode. 59 60;; Font Lock Support: 61;; 62;; If you are using global-font-lock-mode (introduced in Emacs 19.31), 63;; fontification in Metafont and/or MetaPost mode will be activated 64;; automatically. To speed up fontification for the rather complex 65;; patterns used in these modes, it may be a good idea to activate 66;; lazy-lock as a font-lock-support-mode (introduced in Emacs 19.32) 67;; by adding these lines to your startup file: 68;; 69;; (global-font-lock-mode t) 70;; (setq font-lock-support-mode 'lazy-lock-mode) 71;; 72;; If you are using an older version of Emacs, which doesn't provide 73;; global-font-lock-mode or font-lock-support-mode, you can also 74;; activate fontification in Metafont and/or MetaPost mode by adding 75;; the following lines to your startup file: 76;; 77;; (add-hook 'meta-common-mode-hook 'turn-on-font-lock) 78;; (add-hook 'meta-common-mode-hook 'turn-on-lazy-lock) 79 80;; Customization: 81;; 82;; Following the usual Emacs Lisp coding conventions, the major modes 83;; defined in this package provide several hook variables to allow for 84;; local customization when entering the modes. In particular, there 85;; is a `meta-common-mode-hook' which applies to both modes as well as 86;; `metafont-mode-hook' and `metapost-mode-hook' which apply to the 87;; individual modes. In addition, there are several variables and 88;; regexps controlling e.g. the behavior of the indentation function, 89;; which may be customized via `edit-options'. Please refer to the 90;; docstrings in the code below for details. 91 92;; Availability: 93;; 94;; This package is currently available via my "TeX Software" WWW page: 95;; 96;; http://www.thphy.uni-duesseldorf.de/~vieth/subjects/tex/software.html 97;; 98;; As of this version 1.0, this package will be uploaded to CTAN 99;; archives, where it shall find a permanent home, presumably in 100;; tex-archive/support/emacs-modes. It will also be submitted for 101;; integration into the GNU Emacs distribution at that time. 102;; 103;; History: 104;; 105;; v 0.0 -- 1997/02/01 UV Started writing meta-mode.el. 106;; v 0.1 -- 1997/02/02 UV Added preliminary set of font-lock patterns. 107;; v 0.2 -- 1997/02/03 UV Improved and debugged font-lock patterns. 108;; Added indent-line-function for TAB. 109;; v 0.3 -- 1997/02/17 UV Improved font-lock patterns and syntax table. 110;; Improved and debbuged indentation function. 111;; v 0.4 -- 1997/02/18 UV Added functions to indent regions for M-C-q, 112;; also added a preliminary mode-specific menu. 113;; v 0.5 -- 1997/02/19 UV Added functions to skip to next or previous 114;; defun and to re-indent or comment-out defuns. 115;; v 0.6 -- 1997/02/20 UV More debugging, testing and clean-up. 116;; v 0.7 -- 1997/02/22 UV Use easymenu to define mode-specific menu. 117;; v 0.8 -- 1997/02/24 UV Added completion function for M-TAB. 118;; v 0.9 -- 1997/03/08 UV Added fill-paragraph function for comments. 119;; Also fixed a few remaining font-lock problems. 120;; Added meta-mode-load-hook to load meta-buf.el. 121;; v 1.0 -- 1997/04/07 UV Cleanup for official public release. 122;; 123;; Historical Footnote: 124;; 125;; This package was begun on February 1, 1997, exactly 20 years after 126;; the genesis of TeX took place according to Don Knuth's own account 127;; (cf. ``The Errors of TeX'', reprinted in ``Literate Programming'', 128;; Chapter 10, p. 249). What better date could there be to choose? 129;; 130 131 132;;; Code: 133 134(require 'easymenu) 135 136(defgroup meta-font nil 137 "Major mode for editing Metafont or MetaPost sources." 138 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces) 139 :group 'languages) 140 141;;; Fontification. 142 143(defvar meta-font-lock-keywords 144 (let ((input-keywords 145 "\\(input\\|generate\\)") 146 (begin-keywords 147 (concat "\\(begin\\(char\\|fig\\|graph\\|logochar\\)\\|" 148 "\\cmchar\\|dcchar\\|ecchar\\)")) 149 (end-keywords 150 "\\(end\\(char\\|fig\\|graph\\)\\)") 151 (macro-keywords-1 152 "\\(def\\|let\\|mode_def\\|vardef\\)") 153 (macro-keywords-2 154 "\\(primarydef\\|secondarydef\\|tertiarydef\\)") 155;(make-regexp 156; '("expr" "suffix" "text" "primary" "secondary" "tertiary") t) 157 (args-keywords 158 (concat "\\(expr\\|primary\\|s\\(econdary\\|uffix\\)\\|" 159 "te\\(rtiary\\|xt\\)\\)")) 160;(make-regexp 161; '("boolean" "color" "numeric" "pair" "path" "pen" "picture" 162; "string" "transform" "newinternal") t) 163 (type-keywords 164 (concat "\\(boolean\\|color\\|n\\(ewinternal\\|umeric\\)\\|" 165 "p\\(a\\(ir\\|th\\)\\|en\\|icture\\)\\|string\\|" 166 "transform\\)")) 167;(make-regexp 168; '("for" "forever" "forsuffixes" "endfor" 169; "step" "until" "upto" "downto" "thru" "within" 170; "iff" "if" "elseif" "else" "fi" "exitif" "exitunless" 171; "let" "def" "vardef" "enddef" "mode_def" 172; "true" "false" "known" "unknown" "and" "or" "not" 173; "save" "interim" "inner" "outer" "relax" 174; "begingroup" "endgroup" "expandafter" "scantokens" 175; "generate" "input" "endinput" "end" "bye" 176; "message" "errmessage" "errhelp" "special" "numspecial" 177; "readstring" "readfrom" "write") t) 178 (syntactic-keywords 179 (concat "\\(and\\|b\\(egingroup\\|ye\\)\\|" 180 "d\\(ef\\|ownto\\)\\|e\\(lse\\(\\|if\\)" 181 "\\|nd\\(\\|def\\|for\\|group\\|input\\)" 182 "\\|rr\\(help\\|message\\)" 183 "\\|x\\(it\\(if\\|unless\\)\\|pandafter\\)\\)\\|" 184 "f\\(alse\\|i\\|or\\(\\|ever\\|suffixes\\)\\)\\|" 185 "generate\\|i\\(ff?\\|n\\(ner\\|put\\|terim\\)\\)\\|" 186 "known\\|let\\|m\\(essage\\|ode_def\\)\\|" 187 "n\\(ot\\|umspecial\\)\\|o\\(r\\|uter\\)\\|" 188 "re\\(ad\\(from\\|string\\)\\|lax\\)\\|" 189 "s\\(ave\\|cantokens\\|pecial\\|tep\\)\\|" 190 "t\\(hru\\|rue\\)\\|" 191 "u\\(n\\(known\\|til\\)\\|pto\\)\\|" 192 "vardef\\|w\\(ithin\\|rite\\)\\)")) 193 ) 194 (list 195 ;; embedded TeX code in btex ... etex 196 (cons (concat "\\(btex\\|verbatimtex\\)" 197 "[ \t]+\\(.*\\)[ \t]+" 198 "\\(etex\\)") 199 '((1 font-lock-keyword-face) 200 (2 font-lock-string-face) 201 (3 font-lock-keyword-face))) 202 ;; unary macro definitions: def, vardef, let 203 (cons (concat "\\<" macro-keywords-1 "\\>" 204 "[ \t]+\\(\\sw+\\|\\s_+\\|\\s.+\\)") 205 '((1 font-lock-keyword-face) 206 (2 font-lock-function-name-face))) 207 ;; binary macro defintions: <leveldef> x operator y 208 (cons (concat "\\<" macro-keywords-2 "\\>" 209 "[ \t]+\\(\\sw+\\)" 210 "[ \t]*\\(\\sw+\\|\\s.+\\)" 211 "[ \t]*\\(\\sw+\\)") 212 '((1 font-lock-keyword-face) 213 (2 font-lock-variable-name-face nil t) 214 (3 font-lock-function-name-face nil t) 215 (4 font-lock-variable-name-face nil t))) 216 ;; variable declarations: numeric, pair, color, ... 217 (cons (concat "\\<" type-keywords "\\>" 218 "\\([ \t]+\\(\\sw+\\)\\)*") 219 '((1 font-lock-type-face) 220 (font-lock-match-meta-declaration-item-and-skip-to-next 221 (goto-char (match-end 1)) nil 222 (1 font-lock-variable-name-face nil t)))) 223 ;; argument declarations: expr, suffix, text, ... 224 (cons (concat "\\<" args-keywords "\\>" 225 "\\([ \t]+\\(\\sw+\\|\\s_+\\)\\)*") 226 '((1 font-lock-type-face) 227 (font-lock-match-meta-declaration-item-and-skip-to-next 228 (goto-char (match-end 1)) nil 229 (1 font-lock-variable-name-face nil t)))) 230 ;; special case of arguments: expr x of y 231 (cons (concat "\\(expr\\)[ \t]+\\(\\sw+\\)" 232 "[ \t]+\\(of\\)[ \t]+\\(\\sw+\\)") 233 '((1 font-lock-type-face) 234 (2 font-lock-variable-name-face) 235 (3 font-lock-keyword-face nil t) 236 (4 font-lock-variable-name-face nil t))) 237 ;; syntactic keywords 238 (cons (concat "\\<" syntactic-keywords "\\>") 239 'font-lock-keyword-face) 240 ;; beginchar, beginfig 241 (cons (concat "\\<" begin-keywords "\\>") 242 'font-lock-keyword-face) 243 ;; endchar, endfig 244 (cons (concat "\\<" end-keywords "\\>") 245 'font-lock-keyword-face) 246 ;; input, generate 247 (cons (concat "\\<" input-keywords "\\>" 248 "[ \t]+\\(\\sw+\\)") 249 '((1 font-lock-keyword-face) 250 (2 font-lock-constant-face))) 251 ;; embedded Metafont/MetaPost code in comments 252 (cons "|\\([^|]+\\)|" 253 '(1 font-lock-constant-face t)) 254 )) 255 "Default expressions to highlight in Metafont or MetaPost mode.") 256 257 258(defun font-lock-match-meta-declaration-item-and-skip-to-next (limit) 259 ;; Match and move over Metafont/MetaPost declaration item after point. 260 ;; 261 ;; The expected syntax of an item is either "word" or "symbol", 262 ;; possibly ending with optional whitespace. Everything following 263 ;; the item (but belonging to it) is expected to by skipable by 264 ;; `forward-sexp'. The list of items is expected to be separated 265 ;; by commas and terminated by semicolons or equals signs. 266 ;; 267 (if (looking-at "[ \t]*\\(\\sw+\\|\\s_+\\)") 268 (save-match-data 269 (condition-case nil 270 (save-restriction 271 ;; Restrict to end of line, currently guaranteed to be LIMIT. 272 (narrow-to-region (point-min) limit) 273 (goto-char (match-end 1)) 274 ;; Move over any item value, etc., to the next item. 275 (while (not (looking-at "[ \t]*\\(\\(,\\)\\|;\\|=\\|$\\)")) 276 (goto-char (or (scan-sexps (point) 1) (point-max)))) 277 (goto-char (match-end 2))) 278 (error t))))) 279 280 281 282;;; Completion. 283 284;; The data used to prepare the following lists of primitives and 285;; standard macros available in Metafont or MetaPost was extracted 286;; from the original sources like this: 287;; 288;; grep '^primitive' texk-7.0/web2c/{mf,mp}.web |\ 289;; sed 's/primitive(\("[a-zA-Z]*"\).*/\1/' > {mf,mp}_prim.list 290;; 291;; grep '\(let\|def\|vardef\|primarydef\|secondarydef\|tertiarydef\)' 292;; texmf/meta{font,post}/plain.{mf,mp} > {mf,mp}_plain.list 293 294(defconst meta-common-primitives-list 295 '("ASCII" "addto" "also" "and" "angle" "atleast" "batchmode" 296 "begingroup" "boolean" "boundarychar" "char" "charcode" "chardp" 297 "charexists" "charext" "charht" "charic" "charlist" "charwd" 298 "contour" "controls" "cosd" "curl" "cycle" "day" "decimal" "def" 299 "delimiters" "designsize" "directiontime" "doublepath" "dump" "else" 300 "elseif" "end" "enddef" "endfor" "endgroup" "endinput" "errhelp" 301 "errmessage" "errorstopmode" "everyjob" "exitif" "expandafter" 302 "expr" "extensible" "false" "fi" "floor" "fontdimen" "fontmaking" 303 "for" "forever" "forsuffixes" "headerbyte" "hex" "if" "inner" 304 "input" "interim" "intersectiontimes" "jobname" "kern" "known" 305 "length" "let" "ligtable" "makepath" "makepen" "message" "mexp" 306 "mlog" "month" "newinternal" "nonstopmode" "normaldeviate" "not" 307 "nullpen" "nullpicture" "numeric" "oct" "odd" "of" "or" "outer" 308 "pair" "path" "pausing" "pen" "pencircle" "penoffset" "picture" 309 "point" "postcontrol" "precontrol" "primary" "primarydef" "quote" 310 "randomseed" "readstring" "reverse" "rotated" "save" "scaled" 311 "scantokens" "scrollmode" "secondary" "secondarydef" "shifted" 312 "shipout" "show" "showdependencies" "showstats" "showstopping" 313 "showtoken" "showvariable" "sind" "skipto" "slanted" "special" 314 "sqrt" "step" "str" "string" "subpath" "substring" "suffix" 315 "tension" "tertiary" "tertiarydef" "text" "time" "to" 316 "tracingcapsules" "tracingchoices" "tracingcommands" 317 "tracingequations" "tracingmacros" "tracingonline" "tracingoutput" 318 "tracingrestores" "tracingspecs" "tracingstats" "tracingtitles" 319 "transform" "transformed" "true" "turningnumber" "uniformdeviate" 320 "unknown" "until" "vardef" "warningcheck" "withpen" "xpart" 321 "xscaled" "xxpart" "xypart" "year" "ypart" "yscaled" "yxpart" 322 "yypart" "zscaled") 323 "List of primitives common to Metafont and MetaPost.") 324 325(defconst metafont-primitives-list 326 '("at" "autorounding" "chardx" "chardy" "cull" "display" 327 "dropping" "fillin" "from" "granularity" "hppp" "inwindow" 328 "keeping" "numspecial" "openwindow" "proofing" "smoothing" 329 "totalweight" "tracingedges" "tracingpens" "turningcheck" "vppp" 330 "withweight" "xoffset" "yoffset") 331 "List of primitives only defined in Metafont.") 332 333(defconst metapost-primitives-list 334 '("arclength" "arctime" "bluepart" "bounded" "btex" "clip" 335 "clipped" "color" "dashed" "dashpart" "etex" "filled" "fontpart" 336 "fontsize" "greenpart" "infont" "linecap" "linejoin" "llcorner" 337 "lrcorner" "miterlimit" "mpxbreak" "pathpart" "penpart" 338 "prologues" "readfrom" "redpart" "setbounds" "stroked" "textpart" 339 "textual" "tracinglostchars" "truecorners" "ulcorner" "urcorner" 340 "verbatimtex" "withcolor" "within" "write") 341 "List of primitives only defined in MetaPost.") 342 343(defconst meta-common-plain-macros-list 344 '( "abs" "bot" "bye" "byte" "ceiling" "clear_pen_memory" 345 "clearit" "clearpen" "clearxy" "counterclockwise" "cutdraw" "decr" 346 "dir" "direction" "directionpoint" "div" "dotprod" "downto" "draw" 347 "drawdot" "erase" "exitunless" "fill" "filldraw" "flex" "gobble" 348 "hide" "incr" "interact" "interpath" "intersectionpoint" "inverse" 349 "label" "labels" "lft" "loggingall" "magstep" "makelabel" "max" 350 "min" "mod" "numtok" "penlabels" "penpos" "penstroke" "pickup" 351 "range" "reflectedabout" "relax" "rotatedabout" "rotatedaround" 352 "round" "rt" "savepen" "shipit" "softjoin" "solve" "stop" 353 "superellipse" "takepower" "tensepath" "thru" "top" "tracingall" 354 "tracingnone" "undraw" "undrawdot" "unfill" "unfilldraw" 355 "unitvector" "upto" "whatever") 356 "List of macros common to plain Metafont and MetaPost.") 357 358(defconst metafont-plain-macros-list 359 '("beginchar" "change_width" "culldraw" "cullit" "cutoff" 360 "define_blacker_pixels" "define_corrected_pixels" 361 "define_good_x_pixels" "define_good_y_pixels" 362 "define_horizontal_corrected_pixels" "define_pixels" 363 "define_whole_blacker_pixels" "define_whole_pixels" 364 "define_whole_vertical_blacker_pixels" 365 "define_whole_vertical_pixels" "endchar" "fix_units" 366 "font_coding_scheme" "font_extra_space" "font_identifier" 367 "font_normal_shrink" "font_normal_space" "font_normal_stretch" 368 "font_quad" "font_size" "font_slant" "font_x_height" "gfcorners" 369 "good.bot" "good.lft" "good.rt" "good.top" "good.x" "good.y" 370 "grayfont" "hround" "imagerules" "italcorr" "labelfont" 371 "lowres_fix" "makebox" "makegrid" "maketicks" "mode_lowres" 372 "mode_proof" "mode_setup" "mode_smoke" "nodisplays" "notransforms" 373 "openit" "penrazor" "pensquare" "proofoffset" "proofrule" 374 "proofrulethickness" "screenchars" "screenrule" "screenstrokes" 375 "showit" "slantfont" "smode" "titlefont" "vround") 376 "List of macros only defined in plain Metafont.") 377 378(defconst metapost-plain-macros-list 379 '("arrowhead" "bbox" "beginfig" "buildcycle" "center" "cutafter" 380 "cutbefore" "dashpattern" "dotlabel" "dotlabels" "drawarrow" 381 "drawdblarrow" "drawoptions" "endfig" "image" "label" "off" "on" 382 "thelabel") 383 "List of macros only defined in plain MetaPost.") 384 385(defconst metapost-graph-macros-list 386 '("augment" "auto.x" "auto.y" "autogrid" "begingraph" "endgraph" 387 "format" "frame" "gdata" "gdotlabel" "gdraw" "gdrawarrow" 388 "gdrawdblarrow" "gfill" "glabel" "grid" "itick" "otick" "plot" 389 "setcoords" "setrange") 390 "List of macros only defined in MetaPost \"graph\" package.") 391 392(defconst metapost-boxes-macros-list 393 '("boxit" "boxjoin" "bpath" "circleit" "drawboxed" "drawboxes" 394 "drawunboxed" "fixpos" "fixsize" "pic" "rboxit") 395 "List of macros only defined in MetaPost \"boxes\" package.") 396 397 398(defvar metafont-symbol-list 399 (append meta-common-primitives-list 400 metafont-primitives-list 401 meta-common-plain-macros-list 402 metafont-plain-macros-list) 403 "List of known symbols to complete in Metafont mode.") 404 405(defvar metapost-symbol-list 406 (append meta-common-primitives-list 407 metapost-primitives-list 408 meta-common-plain-macros-list 409 metapost-plain-macros-list 410 metapost-graph-macros-list 411 metapost-boxes-macros-list) 412 "List of known symbols to complete in MetaPost mode.") 413 414 415(defvar meta-symbol-list nil 416 "List of known symbols to complete in Metafont or MetaPost mode.") 417 418(defvar meta-symbol-changed nil 419 "Flag indicating whether `meta-symbol-list' has been initialized.") 420 421(defvar meta-complete-list nil 422; (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list) 423; (list "" 'ispell-complete-word)) 424 "List of ways to perform completion in Metafont or MetaPost mode. 425 426Each entry is a list with the following elements: 4271. Regexp matching the preceding text. 4282. A number indicating the subgroup in the regexp containing the text. 4293. A function returning an alist of possible completions. 4304. Text to append after a succesful completion (if any). 431 432Or alternatively: 4331. Regexp matching the preceding text. 4342. Function to do the actual completion.") 435 436 437(defun meta-add-symbols (&rest entries) 438 "Add entries to list of known symbols in Metafont or MetaPost mode." 439 (if meta-symbol-changed 440 (setq meta-symbol-list (cons entries meta-symbol-list)) 441 (setq meta-symbol-changed t) 442 (setq meta-symbol-list (cons entries meta-symbol-list)))) 443 444(defun meta-symbol-list () 445 "Return value of list of known symbols in Metafont or MetaPost mode. 446If the list was changed, sort the list and remove duplicates first." 447 (if (not meta-symbol-changed) 448 () 449 (setq meta-symbol-changed nil) 450 (message "Preparing completion list...") 451 ;; sort list of symbols 452 (setq meta-symbol-list 453 (sort (mapcar 'meta-listify (apply 'append meta-symbol-list)) 454 'meta-car-string-lessp)) 455 ;; remove duplicates 456 (let ((entry meta-symbol-list)) 457 (while (and entry (cdr entry)) 458 (let ((this (car entry)) 459 (next (car (cdr entry)))) 460 (if (not (string-equal (car this) (car next))) 461 (setq entry (cdr entry)) 462 (if (> (length next) (length this)) 463 (setcdr this (cdr next))) 464 (setcdr entry (cdr (cdr entry))))))) 465 (message "Preparing completion list... done")) 466 meta-symbol-list) 467 468(defun meta-listify (a) 469 ;; utility function used in `meta-add-symbols' 470 (if (listp a) a (list a))) 471 472(defun meta-car-string-lessp (a b) 473 ;; utility function used in `meta-add-symbols' 474 (string-lessp (car a) (car b))) 475 476 477(defun meta-complete-symbol () 478 "Perform completion on Metafont or MetaPost symbol preceding point." 479 (interactive "*") 480 (let ((list meta-complete-list) 481 entry) 482 (while list 483 (setq entry (car list) 484 list (cdr list)) 485 (if (meta-looking-at-backward (car entry) 200) 486 (setq list nil))) 487 (if (numberp (nth 1 entry)) 488 (let* ((sub (nth 1 entry)) 489 (close (nth 3 entry)) 490 (begin (match-beginning sub)) 491 (end (match-end sub)) 492 (pattern (meta-match-buffer 0)) 493 (symbol (buffer-substring begin end)) 494 (list (funcall (nth 2 entry))) 495 (completion (try-completion symbol list))) 496 (cond ((eq completion t) 497 (and close 498 (not (looking-at (regexp-quote close))) 499 (insert close))) 500 ((null completion) 501 (error "Can't find completion for \"%s\"" pattern)) 502 ((not (string-equal symbol completion)) 503 (delete-region begin end) 504 (insert completion) 505 (and close 506 (eq (try-completion completion list) t) 507 (not (looking-at (regexp-quote close))) 508 (insert close))) 509 (t 510 (message "Making completion list...") 511 (let ((list (all-completions symbol list nil))) 512 (with-output-to-temp-buffer "*Completions*" 513 (display-completion-list list symbol))) 514 (message "Making completion list... done")))) 515 (funcall (nth 1 entry))))) 516 517 518(defun meta-looking-at-backward (regexp &optional limit) 519 ;; utility function used in `meta-complete-symbol' 520 (let ((pos (point))) 521 (save-excursion 522 (and (re-search-backward 523 regexp (if limit (max (point-min) (- (point) limit))) t) 524 (eq (match-end 0) pos))))) 525 526(defun meta-match-buffer (n) 527 ;; utility function used in `meta-complete-symbol' 528 (if (match-beginning n) 529 (let ((str (buffer-substring (match-beginning n) (match-end n)))) 530 (set-text-properties 0 (length str) nil str) 531 (copy-sequence str)) 532 "")) 533 534 535 536;;; Indentation. 537 538(defcustom meta-indent-level 2 539 "*Indentation of begin-end blocks in Metafont or MetaPost mode." 540 :type 'integer 541 :group 'meta-font) 542 543 544(defcustom meta-left-comment-regexp "%%+" 545 "*Regexp matching comments that should be placed on the left margin." 546 :type 'regexp 547 :group 'meta-font) 548 549(defcustom meta-right-comment-regexp nil 550 "*Regexp matching comments that should be placed to the right margin." 551 :type '(choice regexp 552 (const :tag "None" nil)) 553 :group 'meta-font) 554 555(defcustom meta-ignore-comment-regexp "%[^%]" 556 "*Regexp matching comments that whose indentation should not be touched." 557 :type 'regexp 558 :group 'meta-font) 559 560 561(defcustom meta-begin-environment-regexp 562 (concat "\\(begin\\(char\\|fig\\|gr\\(aph\\|oup\\)\\|logochar\\)\\|" 563 "def\\|for\\(\\|ever\\|suffixes\\)\\|if\\|mode_def\\|" 564 "primarydef\\|secondarydef\\|tertiarydef\\|vardef\\)") 565 "*Regexp matching the beginning of environments to be indented." 566 :type 'regexp 567 :group 'meta-font) 568 569(defcustom meta-end-environment-regexp 570 (concat "\\(end\\(char\\|def\\|f\\(ig\\|or\\)\\|gr\\(aph\\|oup\\)\\)" 571 "\\|fi\\)") 572 "*Regexp matching the end of environments to be indented." 573 :type 'regexp 574 :group 'meta-font) 575 576(defcustom meta-within-environment-regexp 577; (concat "\\(e\\(lse\\(\\|if\\)\\|xit\\(if\\|unless\\)\\)\\)") 578 (concat "\\(else\\(\\|if\\)\\)") 579 "*Regexp matching keywords within environments not to be indented." 580 :type 'regexp 581 :group 'meta-font) 582 583 584(defun meta-comment-indent () 585 "Return the indentation for a comment in Metafont or MetaPost mode." 586 (if (and meta-left-comment-regexp 587 (looking-at meta-left-comment-regexp)) 588 (current-column) 589 (skip-chars-backward "\t ") 590 (max (if (bolp) 0 (1+ (current-column))) 591 comment-column))) 592 593(defun meta-indent-line () 594 "Indent the line containing point as Metafont or MetaPost source." 595 (interactive) 596 (let ((indent (meta-indent-calculate))) 597 (save-excursion 598 (if (/= (current-indentation) indent) 599 (let ((beg (progn (beginning-of-line) (point))) 600 (end (progn (back-to-indentation) (point)))) 601 (delete-region beg end) 602 (indent-to indent)))) 603 (if (< (current-column) indent) 604 (back-to-indentation)))) 605 606(defun meta-indent-calculate () 607 "Return the indentation of current line of Metafont or MetaPost source." 608 (save-excursion 609 (back-to-indentation) 610 (cond 611 ;; Comments to the left margin. 612 ((and meta-left-comment-regexp 613 (looking-at meta-left-comment-regexp)) 614 0) 615 ;; Comments to the right margin. 616 ((and meta-right-comment-regexp 617 (looking-at meta-right-comment-regexp)) 618 comment-column) 619 ;; Comments best left alone. 620 ((and meta-ignore-comment-regexp 621 (looking-at meta-ignore-comment-regexp)) 622 (current-indentation)) 623 ;; Backindent at end of environments. 624 ((looking-at 625 (concat "\\<" meta-end-environment-regexp "\\>")) 626 (- (meta-indent-calculate-last) meta-indent-level)) 627 ;; Backindent at keywords within environments. 628 ((looking-at 629 (concat "\\<" meta-within-environment-regexp "\\>")) 630 (- (meta-indent-calculate-last) meta-indent-level)) 631 (t (meta-indent-calculate-last))))) 632 633(defun meta-indent-calculate-last () 634 "Return the indentation of previous line of Metafont or MetaPost source." 635 (save-restriction 636 (widen) 637 (skip-chars-backward "\n\t ") 638 (move-to-column (current-indentation)) 639 ;; Ignore comments. 640 (while (and (looking-at comment-start) (not (bobp))) 641 (skip-chars-backward "\n\t ") 642 (if (not (bobp)) 643 (move-to-column (current-indentation)))) 644 (cond 645 ((bobp) 0) 646 (t (+ (current-indentation) 647 (meta-indent-level-count) 648 (cond 649 ;; Compensate for backindent at end of environments. 650 ((looking-at 651 (concat "\\<"meta-end-environment-regexp "\\>")) 652 meta-indent-level) 653 ;; Compensate for backindent within environments. 654 ((looking-at 655 (concat "\\<" meta-within-environment-regexp "\\>")) 656 meta-indent-level) 657 (t 0))))) 658 )) 659 660(defun meta-indent-level-count () 661 "Count indentation change for begin-end commands in the current line." 662 (save-excursion 663 (save-restriction 664 (let ((count 0)) 665 (narrow-to-region 666 (point) (save-excursion 667 (re-search-forward "[^\\\\\"]%\\|\n\\|\\'" nil t) 668 (backward-char) (point))) 669 (while (re-search-forward "\\<\\sw+\\>\\|(\\|)" nil t) 670 (save-excursion 671 (goto-char (match-beginning 0)) 672 (cond 673 ;; Count number of begin-end keywords within line. 674 ((looking-at 675 (concat "\\<" meta-begin-environment-regexp "\\>")) 676 (setq count (+ count meta-indent-level))) 677 ((looking-at 678 (concat "\\<" meta-end-environment-regexp "\\>")) 679 (setq count (- count meta-indent-level))) 680 ;; Count number of open-close parentheses within line. 681 ((looking-at "(") 682 (setq count (+ count meta-indent-level))) 683 ((looking-at ")") 684 (setq count (- count meta-indent-level))) 685 ))) 686 count)))) 687 688 689 690;;; Editing commands. 691 692(defcustom meta-begin-defun-regexp 693 (concat "\\(begin\\(char\\|fig\\|logochar\\)\\|def\\|mode_def\\|" 694 "primarydef\\|secondarydef\\|tertiarydef\\|vardef\\)") 695 "*Regexp matching beginning of defuns in Metafont or MetaPost mode." 696 :type 'regexp 697 :group 'meta-font) 698 699(defcustom meta-end-defun-regexp 700 (concat "\\(end\\(char\\|def\\|fig\\)\\)") 701 "*Regexp matching the end of defuns in Metafont or MetaPost mode." 702 :type 'regexp 703 :group 'meta-font) 704 705 706(defun meta-beginning-of-defun (&optional arg) 707 "Move backward to beginnning of a defun in Metafont or MetaPost code. 708With numeric argument, do it that many times. 709Negative arg -N means move forward to Nth following beginning of defun. 710Returns t unless search stops due to beginning or end of buffer." 711 (interactive "p") 712 (if (or (null arg) (= 0 arg)) (setq arg 1)) 713 (and arg (< arg 0) (not (eobp)) (forward-char 1)) 714 (and (re-search-backward 715 (concat "\\<" meta-begin-defun-regexp "\\>") nil t arg) 716 (progn (goto-char (match-beginning 0)) 717 (skip-chars-backward "%") 718 (skip-chars-backward " \t") t))) 719 720(defun meta-end-of-defun (&optional arg) 721 "Move forward to end of a defun in Metafont or MetaPost code. 722With numeric argument, do it that many times. 723Negative argument -N means move back to Nth preceding end of defun. 724Returns t unless search stops due to beginning or end of buffer." 725 (interactive "p") 726 (if (or (null arg) (= 0 arg)) (setq arg 1)) 727 (and (< arg 0) (not (bobp)) (forward-line -1)) 728 (and (re-search-forward 729 (concat "\\<" meta-end-defun-regexp "\\>") nil t arg) 730 (progn (goto-char (match-end 0)) 731 (skip-chars-forward ";") 732 (skip-chars-forward " \t") 733 (if (looking-at "\n") (forward-line 1)) t))) 734 735 736(defun meta-comment-region (beg end &optional arg) 737 "Comment out active region as Metafont or MetaPost source." 738 (interactive "r") 739 (comment-region beg end arg)) 740 741(defun meta-uncomment-region (beg end) 742 "Uncomment active region as Metafont or MetaPost source." 743 (interactive "r") 744 (comment-region beg end -1)) 745 746(defun meta-comment-defun (&optional arg) 747 "Comment out current environment as Metafont or MetaPost source. 748With prefix argument, uncomment the environment. 749The environment used is the one that contains point or follows point." 750 (interactive "P") 751 (save-excursion 752 (let* ((end (if (meta-end-of-defun) (point) (point-max))) 753 (beg (if (meta-beginning-of-defun) (point) (point-min)))) 754 (comment-region beg end arg)))) 755 756(defun meta-uncomment-defun () 757 "Uncomment current environment as Metafont or MetaPost source." 758 (interactive) 759 (meta-comment-defun -1)) 760 761 762(defun meta-indent-region (beg end) 763 "Indent the active region as Metafont or MetaPost source." 764 (interactive "r") 765 (indent-region beg end nil)) 766 767(defun meta-indent-buffer () 768 "Indent the whole buffer contents as Metafont or MetaPost source." 769 (interactive) 770 (save-excursion 771 (indent-region (point-min) (point-max) nil))) 772 773(defun meta-indent-defun () 774 "Indent the current environment as Metafont or MetaPost source. 775The environment indented is the one that contains point or follows point." 776 (interactive) 777 (save-excursion 778 (let* ((end (if (meta-end-of-defun) (point) (point-max))) 779 (beg (if (meta-beginning-of-defun) (point) (point-min)))) 780 (indent-region beg end nil)))) 781 782 783(defun meta-mark-defun () 784 "Put mark at end of the environment, point at the beginning. 785The environment marked is the one that contains point or follows point." 786 (interactive) 787 (push-mark (point)) 788 (meta-end-of-defun) 789 (push-mark (point) nil t) 790 (meta-beginning-of-defun)) 791 792 793 794;;; Syntax table, keymap and menu. 795 796(defvar meta-mode-abbrev-table nil 797 "Abbrev table used in Metafont or MetaPost mode.") 798(define-abbrev-table 'meta-mode-abbrev-table ()) 799 800(defvar meta-mode-syntax-table nil 801 "Syntax table used in Metafont or MetaPost mode.") 802(if meta-mode-syntax-table 803 () 804 (setq meta-mode-syntax-table (make-syntax-table)) 805 ;; underscores are word constituents 806 (modify-syntax-entry ?_ "w" meta-mode-syntax-table) 807 ;; miscellaneous non-word symbols 808 (modify-syntax-entry ?# "_" meta-mode-syntax-table) 809 (modify-syntax-entry ?@ "_" meta-mode-syntax-table) 810 (modify-syntax-entry ?$ "_" meta-mode-syntax-table) 811 (modify-syntax-entry ?? "_" meta-mode-syntax-table) 812 (modify-syntax-entry ?! "_" meta-mode-syntax-table) 813 ;; binary operators 814 (modify-syntax-entry ?& "." meta-mode-syntax-table) 815 (modify-syntax-entry ?+ "." meta-mode-syntax-table) 816 (modify-syntax-entry ?- "." meta-mode-syntax-table) 817 (modify-syntax-entry ?/ "." meta-mode-syntax-table) 818 (modify-syntax-entry ?* "." meta-mode-syntax-table) 819 (modify-syntax-entry ?. "." meta-mode-syntax-table) 820 (modify-syntax-entry ?: "." meta-mode-syntax-table) 821 (modify-syntax-entry ?= "." meta-mode-syntax-table) 822 (modify-syntax-entry ?< "." meta-mode-syntax-table) 823 (modify-syntax-entry ?> "." meta-mode-syntax-table) 824 (modify-syntax-entry ?| "." meta-mode-syntax-table) 825 ;; opening and closing delimiters 826 (modify-syntax-entry ?\( "()" meta-mode-syntax-table) 827 (modify-syntax-entry ?\) ")(" meta-mode-syntax-table) 828 (modify-syntax-entry ?\[ "(]" meta-mode-syntax-table) 829 (modify-syntax-entry ?\] ")[" meta-mode-syntax-table) 830 (modify-syntax-entry ?\{ "(}" meta-mode-syntax-table) 831 (modify-syntax-entry ?\} "){" meta-mode-syntax-table) 832 ;; comment character 833 (modify-syntax-entry ?% "<" meta-mode-syntax-table) 834 (modify-syntax-entry ?\n ">" meta-mode-syntax-table) 835 ;; escape character, needed for embedded TeX code 836 (modify-syntax-entry ?\\ "\\" meta-mode-syntax-table) 837 ) 838 839(defvar meta-mode-map nil 840 "Keymap used in Metafont or MetaPost mode.") 841(if meta-mode-map 842 () 843 (setq meta-mode-map (make-sparse-keymap)) 844 (define-key meta-mode-map "\t" 'meta-indent-line) 845 (define-key meta-mode-map "\C-m" 'reindent-then-newline-and-indent) 846 ;; Comment Paragraphs: 847; (define-key meta-mode-map "\M-a" 'backward-sentence) 848; (define-key meta-mode-map "\M-e" 'forward-sentence) 849; (define-key meta-mode-map "\M-h" 'mark-paragraph) 850; (define-key meta-mode-map "\M-q" 'fill-paragraph) 851 ;; Navigation: 852 (define-key meta-mode-map "\M-\C-a" 'meta-beginning-of-defun) 853 (define-key meta-mode-map "\M-\C-e" 'meta-end-of-defun) 854 (define-key meta-mode-map "\M-\C-h" 'meta-mark-defun) 855 ;; Indentation: 856 (define-key meta-mode-map "\M-\C-q" 'meta-indent-defun) 857 (define-key meta-mode-map "\C-c\C-qe" 'meta-indent-defun) 858 (define-key meta-mode-map "\C-c\C-qr" 'meta-indent-region) 859 (define-key meta-mode-map "\C-c\C-qb" 'meta-indent-buffer) 860 ;; Commenting Out: 861 (define-key meta-mode-map "\C-c%" 'meta-comment-defun) 862; (define-key meta-mode-map "\C-uC-c%" 'meta-uncomment-defun) 863 (define-key meta-mode-map "\C-c;" 'meta-comment-region) 864 (define-key meta-mode-map "\C-c:" 'meta-uncomment-region) 865 ;; Symbol Completion: 866 (define-key meta-mode-map "\M-\t" 'meta-complete-symbol) 867 ;; Shell Commands: 868; (define-key meta-mode-map "\C-c\C-c" 'meta-command-file) 869; (define-key meta-mode-map "\C-c\C-k" 'meta-kill-job) 870; (define-key meta-mode-map "\C-c\C-l" 'meta-recenter-output) 871 ) 872 873(easy-menu-define 874 meta-mode-menu meta-mode-map 875 "Menu used in Metafont or MetaPost mode." 876 (list "Meta" 877 ["Forward Environment" meta-beginning-of-defun t] 878 ["Backward Environment" meta-end-of-defun t] 879 "--" 880 ["Indent Line" meta-indent-line t] 881 ["Indent Environment" meta-indent-defun t] 882 ["Indent Region" meta-indent-region 883 :active (meta-mark-active)] 884 ["Indent Buffer" meta-indent-buffer t] 885 "--" 886 ["Comment Out Environment" meta-comment-defun t] 887 ["Uncomment Environment" meta-uncomment-defun t] 888 ["Comment Out Region" meta-comment-region 889 :active (meta-mark-active)] 890 ["Uncomment Region" meta-uncomment-region 891 :active (meta-mark-active)] 892 "--" 893 ["Complete Symbol" meta-complete-symbol t] 894; "--" 895; ["Command on Buffer" meta-command-file t] 896; ["Kill Job" meta-kill-job t] 897; ["Recenter Output Buffer" meta-recenter-output-buffer t] 898 )) 899 900;; Compatibility: XEmacs doesn't have the `mark-active' variable. 901(defun meta-mark-active () 902 "Return whether the mark and region are currently active in this buffer." 903 (if (boundp 'mark-active) mark-active (mark))) 904 905 906 907;;; Hook variables. 908 909(defcustom meta-mode-load-hook nil 910 "*Hook evaluated when first loading Metafont or MetaPost mode." 911 :type 'hook 912 :group 'meta-font) 913 914(defcustom meta-common-mode-hook nil 915 "*Hook evaluated by both `metafont-mode' and `metapost-mode'." 916 :type 'hook 917 :group 'meta-font) 918 919(defcustom metafont-mode-hook nil 920 "*Hook evaluated by `metafont-mode' after `meta-common-mode-hook'." 921 :type 'hook 922 :group 'meta-font) 923(defcustom metapost-mode-hook nil 924 "*Hook evaluated by `metapost-mode' after `meta-common-mode-hook'." 925 :type 'hook 926 :group 'meta-font) 927 928 929 930;;; Initialization. 931 932(defun meta-common-initialization () 933 "Common initialization for Metafont or MetaPost mode." 934 (kill-all-local-variables) 935 936 (make-local-variable 'paragraph-start) 937 (make-local-variable 'paragraph-separate) 938 (setq paragraph-start 939 (concat page-delimiter "\\|$")) 940 (setq paragraph-separate 941 (concat page-delimiter "\\|$")) 942 943 (make-local-variable 'paragraph-ignore-fill-prefix) 944 (setq paragraph-ignore-fill-prefix t) 945 946 (make-local-variable 'comment-start-skip) 947 (make-local-variable 'comment-start) 948 (make-local-variable 'comment-end) 949 (make-local-variable 'comment-multi-line) 950 (setq comment-start-skip "%+[ \t]*") 951 (setq comment-start "%") 952 (setq comment-end "") 953 (setq comment-multi-line nil) 954 955 (make-local-variable 'parse-sexp-ignore-comments) 956 (setq parse-sexp-ignore-comments t) 957 958 (make-local-variable 'comment-indent-function) 959 (setq comment-indent-function 'meta-comment-indent) 960 (make-local-variable 'indent-line-function) 961 (setq indent-line-function 'meta-indent-line) 962 ;; No need to define a mode-specific 'indent-region-function. 963 ;; Simply use the generic 'indent-region and 'comment-region. 964 965 ;; Set defaults for font-lock mode. 966 (make-local-variable 'font-lock-defaults) 967 (setq font-lock-defaults 968 '(meta-font-lock-keywords 969 nil nil ((?_ . "w")) nil 970 (font-lock-comment-start-regexp . "%"))) 971 972 ;; Activate syntax table, keymap and menu. 973 (setq local-abbrev-table meta-mode-abbrev-table) 974 (set-syntax-table meta-mode-syntax-table) 975 (use-local-map meta-mode-map) 976 (easy-menu-add meta-mode-menu) 977 ) 978 979 980;;;###autoload 981(defun metafont-mode () 982 "Major mode for editing Metafont sources. 983Special commands: 984\\{meta-mode-map} 985 986Turning on Metafont mode calls the value of the variables 987`meta-common-mode-hook' and `metafont-mode-hook'." 988 (interactive) 989 (meta-common-initialization) 990 (setq mode-name "Metafont") 991 (setq major-mode 'metafont-mode) 992 993 ;; Set defaults for completion function. 994 (make-local-variable 'meta-symbol-list) 995 (make-local-variable 'meta-symbol-changed) 996 (make-local-variable 'meta-complete-list) 997 (setq meta-symbol-list nil) 998 (setq meta-symbol-changed nil) 999 (apply 'meta-add-symbols metafont-symbol-list) 1000 (setq meta-complete-list 1001 (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list) 1002 (list "" 'ispell-complete-word))) 1003 (run-mode-hooks 'meta-common-mode-hook 'metafont-mode-hook)) 1004 1005;;;###autoload 1006(defun metapost-mode () 1007 "Major mode for editing MetaPost sources. 1008Special commands: 1009\\{meta-mode-map} 1010 1011Turning on MetaPost mode calls the value of the variable 1012`meta-common-mode-hook' and `metafont-mode-hook'." 1013 (interactive) 1014 (meta-common-initialization) 1015 (setq mode-name "MetaPost") 1016 (setq major-mode 'metapost-mode) 1017 1018 ;; Set defaults for completion function. 1019 (make-local-variable 'meta-symbol-list) 1020 (make-local-variable 'meta-symbol-changed) 1021 (make-local-variable 'meta-complete-list) 1022 (setq meta-symbol-list nil) 1023 (setq meta-symbol-changed nil) 1024 (apply 'meta-add-symbols metapost-symbol-list) 1025 (setq meta-complete-list 1026 (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list) 1027 (list "" 'ispell-complete-word))) 1028 (run-mode-hooks 'meta-common-mode-hook 'metapost-mode-hook)) 1029 1030 1031;;; Just in case ... 1032 1033(provide 'meta-mode) 1034(run-hooks 'meta-mode-load-hook) 1035 1036;;; arch-tag: ec2916b2-3a83-4cf7-962d-d8019370c006 1037;;; meta-mode.el ends here 1038