]> git.donarmstrong.com Git - lib.git/blob - emacs_el/graphviz-dot-mode.el
fix missing ) for org-mode
[lib.git] / emacs_el / graphviz-dot-mode.el
1 ;;; graphviz-dot-mode.el --- Mode for the dot-language used by graphviz (att).
2
3 ;; Copyright (C) 2002 - 2005 Pieter Pareit <pieter.pareit@scarlet.be>
4
5 ;; This program is free software; you can redistribute it and/or
6 ;; modify it under the terms of the GNU General Public License as
7 ;; published by the Free Software Foundation; either version 2 of
8 ;; the License, or (at your option) any later version.
9
10 ;; This program is distributed in the hope that it will be
11 ;; useful, but WITHOUT ANY WARRANTY; without even the implied
12 ;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 ;; PURPOSE.  See the GNU General Public License for more details.
14
15 ;; You should have received a copy of the GNU General Public
16 ;; License along with this program; if not, write to the Free
17 ;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18 ;; MA 02111-1307 USA
19
20 ;; Authors: Pieter Pareit <pieter.pareit@scarlet.be>
21 ;;          Rubens Ramos <rubensr AT users.sourceforge.net>
22 ;; Maintainer: Pieter Pareit <pieter.pareit@planetinternet.be>
23 ;; Homepage: http://users.skynet.be/ppareit/projects/graphviz-dot-mode/graphviz-dot-mode.html
24 ;; Created: 28 Oct 2002
25 ;; Last modified: 24 Feb 2005
26 ;; Version: 0.3.4
27 ;; Keywords: mode dot dot-language dotlanguage graphviz graphs att
28
29 ;;; Commentary:
30 ;; Use this mode for editing files in the dot-language (www.graphviz.org and
31 ;; http://www.research.att.com/sw/tools/graphviz/).
32 ;;
33 ;; To use graphviz-dot-mode, add 
34 ;; (load-file "PATH_TO_FILE/graphviz-dot-mode.el") 
35 ;; to your ~/.emacs(.el) or ~/.xemacs/init.el
36 ;;
37 ;; The graphviz-dot-mode will do font locking, indentation, preview of graphs
38 ;; and eases compilation/error location. There is support for both GNU Emacs
39 ;; and XEmacs.
40 ;;
41 ;; Font locking is automatic, indentation uses the same commands as
42 ;; other modes, tab, M-j and C-M-q.  Insertion of comments uses the
43 ;; same commands as other modes, M-; .  You can compile a file using
44 ;; M-x compile or C-c c, after that M-x next-error will also work.
45 ;; There is support for viewing an generated image with C-c p.
46
47 ;;; Todo:
48 ;; * cleanup the mess of graphviz-dot-compilation-parse-errors
49 ;; * electric indentation is fundamentally broken, because 
50 ;;   {...} are also used for record nodes. You could argue, I suppose, that 
51 ;;   many diagrams don't need those, but it would be worth having a note (and 
52 ;;   it makes sense that the default is now for electric indentation to be 
53 ;;   off).
54
55 ;;; History:
56
57 ;; Version 0.3.4 bug fixes
58 ;; 24/02/2005: * fixed a bug in graphviz-dot-preview
59 ;; Version 0.3.3 bug fixes
60 ;; 13/02/2005: Reuben Thomas <rrt AT sc3d.org>
61 ;;             * add graphviz-dot-indent-width
62 ;; Version 0.3.2 bug fixes
63 ;; 25/03/2004: Rubens Ramos <rubensr AT users.sourceforge.net>
64 ;;             * semi-colons and brackets are added when electric
65 ;;               behaviour is disabled.
66 ;;             * electric characters do not behave electrically inside
67 ;;               comments or strings.
68 ;;             * default for electric-braces is disabled now (makes more
69 ;;               sense I guess).
70 ;;             * using read-from-minibuffer instead of read-shell-command
71 ;;               for emacs.
72 ;;             * Fixed test for easymenu, so that it works on older
73 ;;               versions of XEmacs.
74 ;;             * Fixed indentation error when trying to indent last brace
75 ;;               of an empty graph.
76 ;;             * region-active-p does not exist in emacs (21.2 at least),
77 ;;               so removed from code
78 ;;             * Added uncomment menu option
79 ;; Version 0.3.1 bug fixes
80 ;; 03/03/2004: * backward-word needs argument for older emacs
81 ;; Version 0.3 added features and fixed bugs
82 ;; 10/01/2004: fixed a bug in graphviz-dot-indent-graph
83 ;; 08/01/2004: Rubens Ramos <rubensr AT users.sourceforge.net>
84 ;;             * added customization support
85 ;;             * Now it works on XEmacs and Emacs
86 ;;             * Added support to use an external Viewer
87 ;;             * Now things do not break when dot mode is entered
88 ;;               when there is no buffer name, but the side effect is
89 ;;               that in this case, the compilation command is not
90 ;;               correct.
91 ;;             * Preview works on XEmacs and emacs.
92 ;;             * Electric indentation on newline
93 ;;             * Minor changes to indentation
94 ;;             * Added keyword completion (but could be A LOT better)
95 ;;             * There are still a couple of ugly hacks. Look for 'RR'.
96 ;; Version 0.2 added features
97 ;; 11/11/2002: added preview support.
98 ;; 10/11/2002: indent a graph or subgraph at once with C-M-q.
99 ;; 08/11/2002: relaxed rules for indentation, the may now be extra chars
100 ;;             after beginning of graph (comment's for example).
101 ;; Version 0.1.2 bug fixes and naming issues
102 ;; 06/11/2002: renamed dot-font-lock-defaults to dot-font-lock-keywords.
103 ;;             added some documentation to dot-colors.
104 ;;             provided a much better way to handle my max-specpdl-size
105 ;;             problem.
106 ;;             added an extra autoload cookie (hope this helps, as I don't
107 ;;             yet use autoload myself)
108 ;; Version 0.1.1 bug fixes
109 ;; 06/11/2002: added an missing attribute, for font-locking to work.
110 ;;             fixed the regex generating, so that it only recognizes
111 ;;             whole words
112 ;; 05/11/2002: there can now be extra white space chars after an '{'.
113 ;; 04/11/2002: Why I use max-specpdl-size is now documented, and old value
114 ;;             gets restored.
115 ;; Version 0.1 initial release
116 ;; 02/11/2002: implemented parser for *compilation* of a .dot file.
117 ;; 01/11/2002: implemented compilation of an .dot file.
118 ;; 31/10/2002: added syntax-table to the mode.
119 ;; 30/10/2002: implemented indentation code.
120 ;; 29/10/2002: implemented all of font-lock.
121 ;; 28/10/2002: derived graphviz-dot-mode from fundamental-mode, started 
122 ;;             implementing font-lock.
123
124 ;;; Code:
125
126 (defconst graphviz-dot-mode-version "0.3.3"
127   "Version of `graphviz-dot-mode.el'.")
128
129 (defgroup graphviz nil
130   "Major mode for editing Graphviz Dot files"
131   :group 'tools)
132
133 (defun graphviz-dot-customize ()
134   "Run \\[customize-group] for the `graphviz' group."
135   (interactive)
136   (customize-group 'graphviz))
137
138 (defvar graphviz-dot-mode-abbrev-table nil
139   "Abbrev table in use in Graphviz Dot mode buffers.")
140 (define-abbrev-table 'graphviz-dot-mode-abbrev-table ())
141
142 (defcustom graphviz-dot-dot-program "dot"
143   "*Location of the dot program. This is used by `compile'."
144   :type 'string
145   :group 'graphviz)
146
147 (defcustom graphviz-dot-view-command "doted %s"
148   "*External program to run on the buffer. You can use `%s' in this string,
149 and it will be substituted by the buffer name."
150   :type 'string
151   :group 'graphviz)
152
153 (defcustom graphviz-dot-view-edit-command nil
154   "*Whether to allow the user to edit the command to run an external
155 viewer."
156   :type 'boolean
157   :group 'graphviz)
158
159 (defcustom graphviz-dot-save-before-view t
160   "*If not nil, M-x graphviz-dot-view saves the current buffer before running
161 the command."
162   :type 'boolean
163   :group 'graphviz)
164
165 (defcustom graphviz-dot-auto-indent-on-newline t
166   "*If not nil, `electric-graphviz-dot-terminate-line' is executed in a line is terminated."
167   :type 'boolean
168   :group 'graphviz)
169
170 (defcustom graphviz-dot-indent-width default-tab-width
171   "*Indentation width in Graphviz Dot mode buffers."
172   :type 'integer
173   :group 'graphviz)
174
175 (defcustom graphviz-dot-auto-indent-on-braces nil
176   "*If not nil, `electric-graphviz-dot-open-brace' and `electric-graphviz-dot-close-brace' are executed when { or } are typed"
177   :type 'boolean
178   :group 'graphviz)
179
180 (defcustom graphviz-dot-auto-indent-on-semi t
181   "*If not nil, `electric-graphviz-dot-semi' is executed when semicolon is typed"
182   :type 'boolean
183   :group 'graphviz)
184
185 (defcustom graphviz-dot-preview-extension "png"
186   "*The extension to use for the compilation and preview commands. The format
187 for the compilation command is 
188 `dot -T<extension> file.dot > file.<extension>'."
189   :type 'string
190   :group 'graphviz)
191
192 (defcustom graphviz-dot-toggle-completions nil
193   "*Non-nil means that repeated use of \
194 \\<graphviz-dot-mode-map>\\[graphviz-dot-complete-word] will toggle the possible
195 completions in the minibuffer.  Normally, when there is more than one possible
196 completion, a buffer will display all completions."
197   :type 'boolean
198   :group 'graphviz)
199
200 (defcustom graphviz-dot-delete-completions nil
201   "*Non-nil means that the completion buffer is automatically deleted when a
202 key is pressed."
203   :type 'boolean
204   :group 'graphviz)
205
206 (defcustom graphviz-dot-attr-keywords 
207   '("graph" "digraph" "subgraph" "node" "edge" "strict" "rankdir"
208     "size" "page" "Damping" "Epsilon" "URL" "arrowhead" "arrowsize"
209     "arrowtail" "bb" "bgcolor" "bottomlabel" "center" "clusterrank"
210     "color" "comment" "compound" "concentrate" "constraint" "decorate"
211     "dim" "dir" "distortion" "fillcolor" "fixedsize" "fontcolor"
212     "fontname" "fontpath" "fontsize" "group" "headURL" "headlabel"
213     "headport" "height" "label" "labelangle" "labeldistance" "labelfloat"
214     "labelfontcolor" "labelfontname" "labelfontsize" "labeljust"
215     "labelloc" "layer" "layers" "len" "lhead" "lp" "ltail" "margin"
216     "maxiter" "mclimit" "minlen" "model" "nodesep" "normalize" "nslimit"
217     "nslimit1" "ordering" "orientation" "overlap" "pack" "pagedir"
218     "pencolor" "peripheries" "pin" "pos" "quantum" "rank" "ranksep"
219     "ratio" "rects" "regular" "remincross" "rotate" "samehead" "sametail"
220     "samplepoint" "searchsize" "sep" "shape" "shapefile" "showboxes"
221     "sides" "skew" "splines" "start" "style" "stylesheet" "tailURL"
222     "taillabel" "tailport" "toplabel" "vertices" "voro_margin" "weight"
223     "z")
224   "*Keywords for attribute names in a graph. This is used by the auto
225 completion code. The actual completion tables are built when the mode
226 is loaded, so changes to this are not immediately visible."
227   :type '(repeat (string :tag "Keyword"))
228   :group 'graphviz)
229
230 (defcustom graphviz-dot-value-keywords 
231   '("true" "false" "normal" "inv" "dot" "invdot" "odot" "invodot"
232     "none" "tee" "empty" "invempty" "diamond" "odiamond" "box" "obox"
233     "open" "crow" "halfopen" "local" "global" "none" "forward" "back"
234     "both" "none" "BL" "BR" "TL" "TR" "RB" "RT" "LB" "LT" ":n" ":ne" ":e"
235     ":se" ":s" ":sw" ":w" ":nw" "same" "min" "source" "max" "sink" "LR"
236     "box" "polygon" "ellipse" "circle" "point" "egg" "triangle"
237     "plaintext" "diamond" "trapezium" "parallelogram" "house" "hexagon"
238     "octagon" "doublecircle" "doubleoctagon" "tripleoctagon" "invtriangle"
239     "invtrapezium" "invhouse" "Mdiamond" "Msquare" "Mcircle" "record"
240     "Mrecord" "dashed" "dotted" "solid" "invis" "bold" "filled"
241     "diagonals" "rounded" ) 
242   "*Keywords for attribute values. This is used by the auto completion
243 code. The actual completion tables are built when the mode is loaded,
244 so changes to this are not immediately visible."
245   :type '(repeat (string :tag "Keyword")) 
246   :group 'graphviz)
247
248 ;;; Font-locking:
249 (defvar graphviz-dot-colors-list
250   '(aliceblue antiquewhite antiquewhite1 antiquewhite2
251         antiquewhite3 antiquewhite4 aquamarine aquamarine1
252         aquamarine2 aquamarine3 aquamarine4 azure azure1
253         azure2 azure3 azure4 beige bisque bisque1 bisque2
254         bisque3 bisque4 black blanchedalmond blue blue1
255         blue2 blue3 blue4 blueviolet brown brown1 brown2
256         brown3 brown4 burlywood burlywood1 burlywood2
257         burlywood3 burlywood4 cadetblue cadetblue1
258         cadetblue2 cadetblue3 cadetblue4 chartreuse
259         chartreuse1 chartreuse2 chartreuse3 chartreuse4
260         chocolate chocolate1 chocolate2 chocolate3 chocolate4
261         coral coral1 coral2 coral3 coral4 cornflowerblue
262         cornsilk cornsilk1 cornsilk2 cornsilk3 cornsilk4
263         crimson cyan cyan1 cyan2 cyan3 cyan4 darkgoldenrod
264         darkgoldenrod1 darkgoldenrod2 darkgoldenrod3
265         darkgoldenrod4 darkgreen darkkhaki darkolivegreen
266         darkolivegreen1 darkolivegreen2 darkolivegreen3
267         darkolivegreen4 darkorange darkorange1 darkorange2
268         darkorange3 darkorange4 darkorchid darkorchid1
269         darkorchid2 darkorchid3 darkorchid4 darksalmon
270         darkseagreen darkseagreen1 darkseagreen2
271         darkseagreen3 darkseagreen4 darkslateblue
272         darkslategray darkslategray1 darkslategray2
273         darkslategray3  darkslategray4 darkslategrey
274         darkturquoise darkviolet deeppink deeppink1
275         deeppink2 deeppink3 deeppink4 deepskyblue
276         deepskyblue1 deepskyblue2 deepskyblue3 deepskyblue4
277         dimgray dimgrey  dodgerblue dodgerblue1 dodgerblue2
278         dodgerblue3  dodgerblue4 firebrick firebrick1
279         firebrick2 firebrick3 firebrick4 floralwhite
280         forestgreen gainsboro ghostwhite gold gold1 gold2
281         gold3 gold4 goldenrod goldenrod1 goldenrod2
282         goldenrod3 goldenrod4 gray gray0 gray1 gray10 gray100
283         gray11 gray12 gray13 gray14 gray15 gray16 gray17
284         gray18 gray19 gray2 gray20 gray21 gray22 gray23
285         gray24 gray25 gray26 gray27 gray28 gray29 gray3
286         gray30 gray31 gray32 gray33 gray34 gray35 gray36
287         gray37 gray38 gray39 gray4 gray40 gray41 gray42
288         gray43 gray44 gray45 gray46 gray47 gray48 gray49
289         gray5 gray50 gray51 gray52 gray53 gray54 gray55
290         gray56 gray57 gray58 gray59 gray6 gray60 gray61
291         gray62 gray63 gray64 gray65 gray66 gray67 gray68
292         gray69 gray7 gray70 gray71 gray72 gray73 gray74
293         gray75 gray76 gray77 gray78 gray79 gray8 gray80
294         gray81 gray82 gray83 gray84 gray85 gray86 gray87
295         gray88 gray89 gray9 gray90 gray91 gray92 gray93
296         gray94 gray95 gray96 gray97 gray98 gray99 green
297         green1 green2 green3 green4 greenyellow grey grey0
298         grey1 grey10 grey100 grey11 grey12 grey13 grey14
299         grey15 grey16 grey17 grey18 grey19 grey2 grey20
300         grey21 grey22 grey23 grey24 grey25 grey26 grey27
301         grey28 grey29 grey3 grey30 grey31 grey32 grey33
302         grey34 grey35 grey36 grey37 grey38 grey39 grey4
303         grey40 grey41 grey42 grey43 grey44 grey45 grey46
304         grey47 grey48 grey49 grey5 grey50 grey51 grey52
305         grey53 grey54 grey55 grey56 grey57 grey58 grey59
306         grey6 grey60 grey61 grey62 grey63 grey64 grey65
307         grey66 grey67 grey68 grey69 grey7 grey70 grey71
308         grey72 grey73 grey74 grey75 grey76 grey77 grey78
309         grey79 grey8 grey80 grey81 grey82 grey83 grey84
310         grey85 grey86 grey87 grey88 grey89 grey9 grey90
311         grey91 grey92 grey93 grey94 grey95 grey96 grey97
312         grey98 grey99 honeydew honeydew1 honeydew2 honeydew3
313         honeydew4 hotpink hotpink1 hotpink2 hotpink3 hotpink4
314         indianred indianred1 indianred2 indianred3 indianred4
315         indigo ivory ivory1 ivory2 ivory3 ivory4 khaki khaki1
316         khaki2 khaki3 khaki4 lavender lavenderblush
317         lavenderblush1 lavenderblush2 lavenderblush3
318         lavenderblush4 lawngreen lemonchiffon lemonchiffon1
319         lemonchiffon2 lemonchiffon3 lemonchiffon4 lightblue
320         lightblue1 lightblue2 lightblue3 lightblue4
321         lightcoral lightcyan lightcyan1 lightcyan2 lightcyan3
322         lightcyan4 lightgoldenrod lightgoldenrod1
323         lightgoldenrod2 lightgoldenrod3 lightgoldenrod4
324         lightgoldenrodyellow lightgray lightgrey lightpink
325         lightpink1 lightpink2 lightpink3 lightpink4
326         lightsalmon lightsalmon1 lightsalmon2 lightsalmon3
327         lightsalmon4 lightseagreen lightskyblue lightskyblue1
328         lightskyblue2 lightskyblue3 lightskyblue4
329         lightslateblue lightslategray lightslategrey
330         lightsteelblue lightsteelblue1 lightsteelblue2
331         lightsteelblue3 lightsteelblue4 lightyellow
332         lightyellow1 lightyellow2 lightyellow3 lightyellow4
333         limegreen linen magenta magenta1 magenta2 magenta3
334         magenta4 maroon maroon1 maroon2 maroon3 maroon4
335         mediumaquamarine mediumblue  mediumorchid
336         mediumorchid1 mediumorchid2 mediumorchid3
337         mediumorchid4 mediumpurple mediumpurple1
338         mediumpurple2 mediumpurple3 mediumpurple4
339         mediumseagreen mediumslateblue mediumspringgreen
340         mediumturquoise mediumvioletred midnightblue
341         mintcream mistyrose mistyrose1 mistyrose2 mistyrose3
342         mistyrose4 moccasin navajowhite navajowhite1
343         navajowhite2 navajowhite3 navajowhite4 navy navyblue
344         oldlace olivedrab olivedrap olivedrab1 olivedrab2
345         olivedrap3 oragne palegoldenrod palegreen palegreen1
346         palegreen2 palegreen3 palegreen4 paleturquoise
347         paleturquoise1 paleturquoise2 paleturquoise3
348         paleturquoise4 palevioletred palevioletred1
349         palevioletred2 palevioletred3 palevioletred4
350         papayawhip peachpuff peachpuff1 peachpuff2
351         peachpuff3 peachpuff4 peru pink pink1 pink2 pink3
352         pink4 plum plum1 plum2 plum3 plum4 powderblue
353         purple purple1 purple2 purple3 purple4 red red1 red2
354         red3 red4 rosybrown rosybrown1 rosybrown2 rosybrown3
355         rosybrown4 royalblue royalblue1 royalblue2 royalblue3
356         royalblue4 saddlebrown salmon salmon1 salmon2 salmon3
357         salmon4 sandybrown seagreen seagreen1 seagreen2
358         seagreen3 seagreen4 seashell seashell1 seashell2
359         seashell3 seashell4 sienna sienna1 sienna2 sienna3
360         sienna4 skyblue skyblue1 skyblue2 skyblue3 skyblue4
361         slateblue slateblue1 slateblue2 slateblue3 slateblue4
362         slategray slategray1 slategray2 slategray3 slategray4
363         slategrey snow snow1 snow2 snow3 snow4 springgreen
364         springgreen1 springgreen2 springgreen3 springgreen4
365         steelblue steelblue1 steelblue2 steelblue3 steelblue4
366         tan tan1 tan2 tan3 tan4 thistle thistle1 thistle2
367         thistle3 thistle4 tomato tomato1 tomato2 tomato3
368         tomato4 transparent turquoise turquoise1 turquoise2
369         turquoise3 turquoise4 violet violetred violetred1
370         violetred2 violetred3 violetred4 wheat wheat1 wheat2
371         wheat3 wheat4 white whitesmoke yellow yellow1 yellow2
372         yellow3 yellow4 yellowgreen)
373   "Possible color constants in the dot language.
374 The list of constant is available at http://www.research.att.com/~erg/graphviz\
375 /info/colors.html")
376
377
378 (defvar graphviz-dot-color-keywords
379   (mapcar 'symbol-name graphviz-dot-colors-list))
380
381 (defvar graphviz-attr-keywords
382   (mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-attr-keywords))
383
384 (defvar graphviz-value-keywords
385   (mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-value-keywords))
386
387 (defvar graphviz-color-keywords
388   (mapcar '(lambda (elm) (cons elm 0)) graphviz-dot-color-keywords))
389
390 ;;; Key map
391 (defvar graphviz-dot-mode-map ()
392   "Keymap used in Graphviz Dot mode.")
393
394 (if graphviz-dot-mode-map
395     ()
396   (let ((map (make-sparse-keymap)))
397     (define-key map "\r"       'electric-graphviz-dot-terminate-line)
398     (define-key map "{"        'electric-graphviz-dot-open-brace)
399     (define-key map "}"        'electric-graphviz-dot-close-brace)
400     (define-key map ";"        'electric-graphviz-dot-semi)
401     (define-key map "\M-\t"    'graphviz-dot-complete-word)
402     (define-key map "\C-\M-q"  'graphviz-dot-indent-graph)
403     (define-key map "\C-cp"    'graphviz-dot-preview)
404     (define-key map "\C-cc"    'compile)
405     (define-key map "\C-cv"    'graphviz-dot-view)
406     (define-key map "\C-c\C-c" 'comment-region)
407     (define-key map "\C-c\C-u" 'graphviz-dot-uncomment-region)
408     (setq graphviz-dot-mode-map map)
409     ))
410
411 ;;; Syntax table
412 (defvar graphviz-dot-mode-syntax-table nil
413   "Syntax table for `graphviz-dot-mode'.")
414
415 (if graphviz-dot-mode-syntax-table
416     ()
417   (let ((st (make-syntax-table)))
418     (modify-syntax-entry ?/  ". 124b" st)
419     (modify-syntax-entry ?*  ". 23"   st)
420     (modify-syntax-entry ?\n "> b"    st)
421     (modify-syntax-entry ?=  "."      st)
422     (modify-syntax-entry ?_  "_"      st)
423     (modify-syntax-entry ?-  "_"      st)
424     (modify-syntax-entry ?>  "."      st)
425     (modify-syntax-entry ?[  "("      st)
426     (modify-syntax-entry ?]  ")"      st)
427     (modify-syntax-entry ?\" "\""     st)
428     (setq graphviz-dot-mode-syntax-table st)
429   ))
430
431 (defvar graphviz-dot-font-lock-keywords
432   `(("\\(:?di\\|sub\\)?graph \\(\\sw+\\)"
433      (2 font-lock-function-name-face))
434     (,(regexp-opt graphviz-dot-value-keywords 'words)
435      . font-lock-reference-face)
436     ;; to build the font-locking for the colors,
437     ;; we need more room for max-specpdl-size,
438     ;; after that we take the list of symbols,
439     ;; convert them to a list of strings, and make
440     ;; an optimized regexp from them
441     (,(let ((max-specpdl-size (max max-specpdl-size 1200)))
442   (regexp-opt graphviz-dot-color-keywords))
443      . font-lock-string-face)
444     (,(concat
445        (regexp-opt graphviz-dot-attr-keywords 'words)
446        "[ \\t\\n]*=")
447      ;; RR - ugly, really, but I dont know why xemacs does not work
448      ;; if I change the next car to "1"...
449      (0 font-lock-variable-name-face)))
450   "Keyword highlighting specification for `graphviz-dot-mode'.")
451
452 ;;;###autoload
453 (defun graphviz-dot-mode ()
454   "Major mode for the dot language. \\<graphviz-dot-mode-map> 
455 TAB indents for graph lines. 
456
457 \\[graphviz-dot-indent-graph]\t- Indentaion function.
458 \\[graphviz-dot-preview]\t- Previews graph in a buffer.
459 \\[graphviz-dot-view]\t- Views graph in an external viewer.
460 \\[graphviz-dot-indent-line]\t- Indents current line of code.
461 \\[graphviz-dot-complete-word]\t- Completes the current word.
462 \\[electric-graphviz-dot-terminate-line]\t- Electric newline.
463 \\[electric-graphviz-dot-open-brace]\t- Electric open braces.
464 \\[electric-graphviz-dot-close-brace]\t- Electric close braces.
465 \\[electric-graphviz-dot-semi]\t- Electric semi colons.
466
467 Variables specific to this mode:
468
469   graphviz-dot-dot-program            (default `dot')
470        Location of the dot program.
471   graphviz-dot-view-command           (default `doted %s')
472        Command to run when `graphviz-dot-view' is executed.
473   graphviz-dot-view-edit-command      (default nil)
474        If the user should be asked to edit the view command.
475   graphviz-dot-save-before-view       (default t)
476        Automatically save current buffer berore `graphviz-dot-view'.
477   graphviz-dot-preview-extension      (default `png')
478        File type to use for `graphviz-dot-preview'.
479   graphviz-dot-auto-indent-on-newline (default t)
480        Whether to run `electric-graphviz-dot-terminate-line' when 
481        newline is entered.
482   graphviz-dot-auto-indent-on-braces (default t)
483        Whether to run `electric-graphviz-dot-open-brace' and
484        `electric-graphviz-dot-close-brace' when braces are 
485        entered.
486   graphviz-dot-auto-indent-on-semi (default t)
487        Whether to run `electric-graphviz-dot-semi' when semi colon
488        is typed.
489   graphviz-dot-toggle-completions  (default nil)
490        If completions should be displayed in the buffer instead of a
491        completion buffer when \\[graphviz-dot-complete-word] is
492        pressed repeatedly.
493
494 This mode can be customized by running \\[graphviz-dot-customize].
495
496 Turning on Graphviz Dot mode calls the value of the variable 
497 `graphviz-dot-mode-hook' with no args, if that value is non-nil."
498   (interactive)
499   (kill-all-local-variables)
500   (use-local-map graphviz-dot-mode-map)
501   (setq major-mode 'graphviz-dot-mode)
502   (setq mode-name "dot")
503   (setq local-abbrev-table graphviz-dot-mode-abbrev-table)
504   (set-syntax-table graphviz-dot-mode-syntax-table)
505   (set (make-local-variable 'indent-line-function) 'graphviz-dot-indent-line)
506   (set (make-local-variable 'comment-start) "//")
507   (set (make-local-variable 'comment-start-skip) "/\\*+ *\\|//+ *")
508   (set (make-local-variable 'font-lock-defaults) 
509        '(graphviz-dot-font-lock-keywords))
510   ;; RR - If user is running this in the scratch buffer, there is no
511   ;; buffer file name...
512   (if (buffer-file-name)
513       (set (make-local-variable 'compile-command) 
514        (concat graphviz-dot-dot-program
515                " -T" graphviz-dot-preview-extension " "
516                buffer-file-name
517                " > "
518                (file-name-sans-extension
519                 buffer-file-name)
520                "." graphviz-dot-preview-extension)))
521   (set (make-local-variable 'compilation-parse-errors-function)
522        'graphviz-dot-compilation-parse-errors)
523   (if dot-menu
524       (easy-menu-add dot-menu))
525   (run-hooks 'graphviz-dot-mode-hook)
526   )
527
528 ;;;; Menu definitions
529
530 (defvar dot-menu nil
531   "Menu for Graphviz Dot Mode.
532 This menu will get created automatically if you have the `easymenu'
533 package. Note that the latest X/Emacs releases contain this package.")
534
535 (and (condition-case nil
536          (require 'easymenu)
537        (error nil))
538      (easy-menu-define
539       dot-menu graphviz-dot-mode-map "Graphviz Mode menu"
540       '("Graphviz"
541         ["Indent Graph"       graphviz-dot-indent-graph     t]
542         ["Comment Out Region" comment-region                (mark)]
543         ["Uncomment Region"   graphviz-dot-uncomment-region (mark)]
544         "-"
545         ["Compile"            compile                       t]
546         ["Preview"            graphviz-dot-preview        
547          (and (buffer-file-name)
548               (not (buffer-modified-p)))]
549         ["External Viewer"    graphviz-dot-view             (buffer-file-name)]
550         "-"
551         ["Customize..."       graphviz-dot-customize        t]
552         )))
553
554 ;;;; Compilation
555
556 ;; note on graphviz-dot-compilation-parse-errors:
557 ;;  It would nicer if we could just use compilation-error-regexp-alist
558 ;;  to do that, 3 options:
559 ;;   - still write dot-compilation-parse-errors, don't build
560 ;;     a return list, but modify the *compilation* buffer
561 ;;     in a way compilation-error-regexp-alist recognizes the
562 ;;     format.
563 ;;     to do that, I should globally change compilation-parse-function
564 ;;     to this function, and call the old value of comp..-parse-fun..
565 ;;     to provide the return value.
566 ;;     two drawbacks are that, every compilation would be run through
567 ;;     this function (performance) and that in autoload there would
568 ;;     be a chance that this function would not yet be known.
569 ;;   - let the compilation run through a filter that would
570 ;;     modify the output of dot or neato:
571 ;;     dot -Tpng input.dot | filter
572 ;;     drawback: ugly, extra work for user, extra decency ...
573 ;;               no-option
574 ;;   - modify dot and neato !!! (PP:15/02/2005 seems to have happend,
575 ;;                                       so version 0.4.0 should clean this mess up!)
576 (defun graphviz-dot-compilation-parse-errors (limit-search find-at-least)
577   "Parse the current buffer for dot errors.
578 See variable `compilation-parse-errors-functions' for interface."
579   (interactive)
580   (save-excursion
581     (set-buffer "*compilation*")
582     (goto-char (point-min))
583     (setq compilation-error-list nil)
584     (let (buffer-of-error)
585       (while (not (eobp))
586   (cond
587    ((looking-at "^dot\\( -[^ ]+\\)* \\(.*\\)")
588     (setq buffer-of-error (find-file-noselect
589          (buffer-substring-no-properties
590           (nth 4 (match-data t))
591           (nth 5 (match-data t))))))
592    ((looking-at ".*:.*line \\([0-9]+\\)")
593     (let ((line-of-error
594      (string-to-number (buffer-substring-no-properties
595             (nth 2 (match-data t))
596             (nth 3 (match-data t))))))
597       (setq compilation-error-list
598       (cons
599        (cons
600         (point-marker)
601         (save-excursion
602           (set-buffer buffer-of-error)
603           (goto-line line-of-error)
604           (beginning-of-line)
605           (point-marker)))
606        compilation-error-list))))
607     (t t))
608   (forward-line 1)) )))
609
610 ;;;;
611 ;;;; Indentation
612 ;;;;
613 (defun graphviz-dot-uncomment-region (begin end)
614         "Uncomments a region of code."
615         (interactive "r")
616         (comment-region begin end '(4)))
617
618 (defun graphviz-dot-indent-line ()
619   "Indent current line of dot code."
620   (interactive)
621   (if (bolp)
622       (graphviz-dot-real-indent-line)
623     (save-excursion
624       (graphviz-dot-real-indent-line))))
625         
626 (defun graphviz-dot-real-indent-line ()
627   "Indent current line of dot code."
628   (beginning-of-line)
629   (cond
630    ((bobp)
631     ;; simple case, indent to 0
632     (indent-line-to 0))
633    ((looking-at "^[ \t]*}[ \t]*$")
634     ;; block closing, deindent relative to previous line
635     (indent-line-to (save-excursion
636                       (forward-line -1)
637                       (max 0 (- (current-indentation) graphviz-dot-indent-width)))))
638    ;; other cases need to look at previous lines
639    (t
640     (indent-line-to (save-excursion
641                       (forward-line -1)
642                       (cond
643                        ((looking-at "\\(^.*{[^}]*$\\)")
644                         ;; previous line opened a block
645                         ;; indent to that line
646                         (+ (current-indentation) graphviz-dot-indent-width))
647                        ((and (not (looking-at ".*\\[.*\\].*"))
648                              (looking-at ".*\\[.*")) ; TODO:PP : can be 1 regex
649                         ;; previous line started filling
650                         ;; attributes, intend to that start
651                         (search-forward "[")
652                         (current-column))
653                        ((and (not (looking-at ".*\\[.*\\].*"))
654                              (looking-at ".*\\].*")) ; TODO:PP : "
655                         ;; previous line stopped filling
656                         ;; attributes, find the line that started
657                         ;; filling them and indent to that line
658                         (while (or (looking-at ".*\\[.*\\].*")
659                                    (not (looking-at ".*\\[.*"))) ; TODO:PP : "
660                           (forward-line -1))
661                         (current-indentation))
662                        (t
663                         ;; default case, indent the
664                         ;; same as previous line
665                         (current-indentation)) ))) )))
666
667 (defun graphviz-dot-indent-graph ()
668   "Indent the graph/digraph/subgraph where point is at.
669 This will first teach the beginning of the graph were point is at, and
670 then indent this and each subgraph in it."
671   (interactive)
672   (save-excursion
673     ;; position point at start of graph
674     (while (not (or (looking-at "\\(^.*{[^}]*$\\)") (bobp)))
675       (forward-line -1))
676     ;; bracket { one +; bracket } one -
677     (let ((bracket-count 0))
678       (while
679           (progn
680             (cond
681              ;; update bracket-count
682              ((looking-at "\\(^.*{[^}]*$\\)")
683               (setq bracket-count (+ bracket-count 1)))
684              ;; update bracket-count
685              ((looking-at "^[ \t]*}[ \t]*$")
686               (setq bracket-count (- bracket-count 1))))
687             ;; indent this line and move on
688             (graphviz-dot-indent-line)
689             (forward-line 1)
690             ;; as long as we are not completed or at end of buffer
691             (and (> bracket-count 0) (not (eobp))))))))
692      
693 ;;;;
694 ;;;; Electric indentation
695 ;;;;
696 (defun graphviz-dot-comment-or-string-p ()
697   (let ((state (parse-partial-sexp (point-min) (point))))
698      (or (nth 4 state) (nth 3 state))))
699
700 (defun graphviz-dot-newline-and-indent ()
701   (save-excursion
702     (beginning-of-line)
703     (skip-chars-forward " \t")
704     (graphviz-dot-indent-line))
705   (delete-horizontal-space)
706   (newline)
707   (graphviz-dot-indent-line))
708
709 (defun electric-graphviz-dot-terminate-line ()
710   "Terminate line and indent next line."
711   (interactive)
712   (if graphviz-dot-auto-indent-on-newline
713       (graphviz-dot-newline-and-indent)
714     (newline)))
715
716 (defun electric-graphviz-dot-open-brace ()
717   "Terminate line and indent next line."
718   (interactive)
719   (insert "{")
720   (if (and graphviz-dot-auto-indent-on-braces
721            (not (graphviz-dot-comment-or-string-p)))
722       (graphviz-dot-newline-and-indent)))
723
724 (defun electric-graphviz-dot-close-brace ()
725   "Terminate line and indent next line."
726   (interactive)
727   (insert "}")
728   (if (and graphviz-dot-auto-indent-on-braces
729            (not (graphviz-dot-comment-or-string-p)))
730       (progn
731         (save-excursion
732           (beginning-of-line)
733           (skip-chars-forward " \t")
734           (graphviz-dot-indent-line))
735         (newline)
736         (graphviz-dot-indent-line))))
737
738 (defun electric-graphviz-dot-semi ()
739   "Terminate line and indent next line."
740   (interactive)
741   (insert ";")
742   (if (and graphviz-dot-auto-indent-on-semi
743            (not (graphviz-dot-comment-or-string-p)))
744       (graphviz-dot-newline-and-indent)))
745
746 ;;;;
747 ;;;; Preview
748 ;;;;
749 (defun graphviz-dot-preview ()
750   "Shows an example of the current dot file in an emacs buffer.
751 This assumes that we are running GNU Emacs or XEmacs under a windowing system.
752 See `image-file-name-extensions' for customizing the files that can be
753 loaded in GNU Emacs, and `image-formats-alist' for XEmacs."
754   (interactive)
755   ;; unsafe to compile ourself, ask it to the user
756   (if (buffer-modified-p)
757       (message "Buffer needs to be compiled.")
758     (if (string-match "XEmacs" emacs-version)
759         ;; things are easier in XEmacs...
760         (find-file-other-window (concat (file-name-sans-extension
761                                          buffer-file-name)
762                                         "." graphviz-dot-preview-extension))
763       ;; run through all the extensions for images
764       (let ((l image-file-name-extensions))
765         (while
766             (let ((f (concat (file-name-sans-extension (buffer-file-name))
767                              "."
768                              (car l))))
769               ;; see if a file matches, might be best also to check
770               ;; if file is up to date TODO:PP
771               (if (file-exists-p f)
772                   (progn (auto-image-file-mode 1)
773                          ;; OK, this is ugly, I would need to 
774                          ;; know how I can reload a file in an existing buffer
775                          (if (get-buffer "*preview*")
776                              (kill-buffer "*preview*"))
777                          (set-buffer (find-file-noselect f))
778                          (rename-buffer "*preview*")
779                          (display-buffer (get-buffer "*preview*"))
780                          ;; stop iterating
781                          '())
782                 ;; will stop iterating when l is nil
783                 (setq l (cdr l)))))
784       ;; each extension tested and nothing found, let user know
785       (when (eq l '())
786         (message "No image found."))))))
787
788 ;;;;
789 ;;;; View
790 ;;;;
791 (defun graphviz-dot-view ()
792   "Runs an external viewer. This creates an external process every time it
793 is executed. If `graphviz-dot-save-before-view' is set, the current
794 buffer is saved before the command is executed."
795   (interactive)
796   (let ((cmd (if graphviz-dot-view-edit-command
797                  (if (string-match "XEmacs" emacs-version)
798                      (read-shell-command "View command: " 
799                                          (format graphviz-dot-view-command
800                                                  (buffer-file-name)))
801                    (read-from-minibuffer "View command: " 
802                                          (format graphviz-dot-view-command
803                                                  (buffer-file-name))))
804                (format graphviz-dot-view-command (buffer-file-name)))))
805     (if graphviz-dot-save-before-view 
806         (save-buffer))
807     (setq novaproc (start-process-shell-command
808                     (downcase mode-name) nil cmd))
809     (message (format "Executing `%s'..." cmd))))
810
811 ;;;;
812 ;;;; Completion
813 ;;;;
814 (defvar graphviz-dot-str nil)
815 (defvar graphviz-dot-all nil)
816 (defvar graphviz-dot-pred nil)
817 (defvar graphviz-dot-buffer-to-use nil)
818 (defvar graphviz-dot-flag nil)
819
820 (defun graphviz-dot-get-state ()
821   "Returns the syntax state of the current point."
822   (let ((state (parse-partial-sexp (point-min) (point))))
823     (cond
824      ((nth 4 state) 'comment)
825      ((nth 3 state) 'string)
826      ((not (nth 1 state)) 'out)
827      (t (save-excursion
828           (skip-chars-backward "^[,=\\[]{};")
829           (backward-char)
830           (cond 
831            ((looking-at "[\\[,]{};") 'attribute)
832            ((looking-at "=") (progn
833                                (backward-word 1)
834                                (if (looking-at "[a-zA-Z]*color")
835                                    'color
836                                  'value)))
837            (t 'other)))))))
838
839 (defun graphviz-dot-get-keywords ()
840   "Return possible completions for a word"
841   (let ((state (graphviz-dot-get-state)))
842     (cond
843      ((equal state 'comment)   ())
844      ((equal state 'string)    ())
845      ((equal state 'out)       graphviz-attr-keywords)
846      ((equal state 'value)     graphviz-value-keywords)
847      ((equal state 'color)     graphviz-color-keywords)
848      ((equal state 'attribute) graphviz-attr-keywords)
849      (t                        graphviz-attr-keywords))))
850
851 (defvar graphviz-dot-last-word-numb 0)
852 (defvar graphviz-dot-last-word-shown nil)
853 (defvar graphviz-dot-last-completions nil)
854
855 (defun graphviz-dot-complete-word ()
856   "Complete word at current point."
857   (interactive)
858   (let* ((b (save-excursion (skip-chars-backward "a-zA-Z0-9_") (point)))
859          (e (save-excursion (skip-chars-forward "a-zA-Z0-9_") (point)))
860          (graphviz-dot-str (buffer-substring b e))
861          (allcomp (if (and graphviz-dot-toggle-completions
862                            (string= graphviz-dot-last-word-shown 
863                                     graphviz-dot-str))
864                       graphviz-dot-last-completions
865                     (all-completions graphviz-dot-str 
866                                      (graphviz-dot-get-keywords))))
867          (match (if graphviz-dot-toggle-completions
868                     "" (try-completion
869                         graphviz-dot-str (mapcar '(lambda (elm)
870                                                     (cons elm 0)) allcomp)))))
871     ;; Delete old string
872     (delete-region b e)
873     
874     ;; Toggle-completions inserts whole labels
875     (if graphviz-dot-toggle-completions
876         (progn
877           ;; Update entry number in list
878           (setq graphviz-dot-last-completions allcomp
879                 graphviz-dot-last-word-numb 
880                 (if (>= graphviz-dot-last-word-numb (1- (length allcomp)))
881                     0
882                   (1+ graphviz-dot-last-word-numb)))
883           (setq graphviz-dot-last-word-shown 
884                 (elt allcomp graphviz-dot-last-word-numb))
885           ;; Display next match or same string if no match was found
886           (if (not (null allcomp))
887               (insert "" graphviz-dot-last-word-shown)
888             (insert "" graphviz-dot-str)
889             (message "(No match)")))
890       ;; The other form of completion does not necessarily do that.
891       
892       ;; Insert match if found, or the original string if no match
893       (if (or (null match) (equal match 't))
894           (progn (insert "" graphviz-dot-str)
895                  (message "(No match)"))
896         (insert "" match))
897       ;; Give message about current status of completion
898       (cond ((equal match 't)
899              (if (not (null (cdr allcomp)))
900                  (message "(Complete but not unique)")
901                (message "(Sole completion)")))
902             ;; Display buffer if the current completion didn't help 
903             ;; on completing the label.
904             ((and (not (null (cdr allcomp))) (= (length graphviz-dot-str)
905                                                 (length match)))
906              (with-output-to-temp-buffer "*Completions*"
907                (display-completion-list allcomp))
908              ;; Wait for a keypress. Then delete *Completion*  window
909              (momentary-string-display "" (point))
910              (if graphviz-dot-delete-completions
911                  (delete-window 
912                   (get-buffer-window (get-buffer "*Completions*"))))
913              )))))
914
915 ;;;###autoload
916 (add-to-list 'auto-mode-alist '("\\.dot\\'" . graphviz-dot-mode))
917
918 ;;; graphviz-dot-mode.el ends here
919