graphviz-dot-mode.el (38097B)
1 ;;; graphviz-dot-mode.el --- Mode for the dot-language used by graphviz (att). 2 3 ;; Copyright (C) 2002 - 2020 Pieter Pareit <pieter.pareit@gmail.com> 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@gmail.com> 21 ;; Rubens Ramos <rubensr AT users.sourceforge.net> 22 ;; Eric Anderson http://www.ece.cmu.edu/~andersoe/ 23 ;; Maintainer: Pieter Pareit <pieter.pareit@gmail.com> 24 ;; Homepage: https://ppareit.github.io/graphviz-dot-mode/ 25 ;; Created: 28 Oct 2002 26 ;; Last modified: 25 Januari 2020 27 ;; Version: 0.4.2 28 ;; Package-Requires: ((emacs "25.0")) 29 ;; Keywords: mode dot dot-language dotlanguage graphviz graphs att 30 31 ;;; Commentary: 32 ;; Use this mode for editing files in the dot-language, see 33 ;; https://www.graphviz.org. 34 ;; 35 ;; To use graphviz-dot-mode, add 36 ;; (use-package graphviz-dot-mode 37 ;; :ensure t) 38 ;; to your ~/.emacs.el file. 39 ;; 40 ;; The graphviz-dot-mode will do font locking, indentation, preview of 41 ;; graphs and eases compilation/error location. Font locking is 42 ;; automatic, indentation uses the same commands as other modes, tab, 43 ;; M-j and C-M-q. Insertion of comments uses the same commands as 44 ;; other modes, M-; . You can compile a file using M-x compile or C-c 45 ;; c, after that M-x next-error will also work. There is support for 46 ;; viewing an generated image with C-c p. 47 ;; 48 ;;; Todo: 49 ;; 50 ;;; History: 51 ;; Version 0.4.2 Pieter Pareit 52 ;; 25/01/2020: * Fix issues 53 ;; * Improve font-locking 54 ;; * Improve completion by implementing company mode 55 ;; * Rewrote basic documentation 56 ;; Version 0.4.1 Pieter Pareit 57 ;; 28/09/2019: * Maintenance, checking documentation, fixing flycheck errors. 58 ;; * Solve next-error for gaphviz 59 ;; * Tag new version 60 ;; Version 0.3.11 Olli Piepponen 61 ;; 29/01/2016: * use define-derived-mode for the mode-definition 62 ;; * add support for a auto-loading live preview work flow 63 ;; Version 0.3.10 Kevin Ryde 64 ;; 25/05/2015: * shell-quote-argument for safety 65 ;; * use read-shell-command whenever available, don't set novaproc 66 ;; Version 0.3.9 Titus Barik <titus AT barik.net> 67 ;; 28/08/2012: * compile-command uses -ofile instead of > 68 ;; Version 0.3.8 new home 69 ;; 27/06/2012: * put graphviz-dot-mode into git, updated links 70 ;; Version 0.3.7 Tim Allen 71 ;; 09/03/2011: * fix spaces in file names when compiling 72 ;; Version 0.3.6 maintenance 73 ;; 19/02/2011: * .gv is the new extension (Pander) 74 ;; * comments can start with # (Pander) 75 ;; * highlight of new keywords (Pander) 76 ;; Version 0.3.5 bug (or at least feature I dislike) fix 77 ;; 11/11/2010: Eric Anderson http://www.ece.cmu.edu/~andersoe/ 78 ;; * Preserve indentation across blank (whitespace-only) lines 79 ;; Version 0.3.4 bug fixes 80 ;; 24/02/2005: * fixed a bug in graphviz-dot-preview 81 ;; Version 0.3.3 bug fixes 82 ;; 13/02/2005: Reuben Thomas <rrt AT sc3d.org> 83 ;; * add graphviz-dot-indent-width 84 ;; Version 0.3.2 bug fixes 85 ;; 25/03/2004: Rubens Ramos <rubensr AT users.sourceforge.net> 86 ;; * semi-colons and brackets are added when electric 87 ;; behaviour is disabled. 88 ;; * electric characters do not behave electrically inside 89 ;; comments or strings. 90 ;; * default for electric-braces is disabled now (makes more 91 ;; sense I guess). 92 ;; * using read-from-minibuffer instead of read-shell-command 93 ;; for emacs. 94 ;; * Fixed test for easymenu, so that it works on older 95 ;; versions of XEmacs. 96 ;; * Fixed indentation error when trying to indent last brace 97 ;; of an empty graph. 98 ;; * region-active-p does not exist in emacs (21.2 at least), 99 ;; so removed from code 100 ;; * Added uncomment menu option 101 ;; Version 0.3.1 bug fixes 102 ;; 03/03/2004: * backward-word needs argument for older emacs 103 ;; Version 0.3 added features and fixed bugs 104 ;; 10/01/2004: fixed a bug in graphviz-dot-indent-graph 105 ;; 08/01/2004: Rubens Ramos <rubensr AT users.sourceforge.net> 106 ;; * added customization support 107 ;; * Now it works on XEmacs and Emacs 108 ;; * Added support to use an external Viewer 109 ;; * Now things do not break when dot mode is entered 110 ;; when there is no buffer name, but the side effect is 111 ;; that in this case, the compilation command is not 112 ;; correct. 113 ;; * Preview works on XEmacs and emacs. 114 ;; * Electric indentation on newline 115 ;; * Minor changes to indentation 116 ;; * Added keyword completion (but could be A LOT better) 117 ;; * There are still a couple of ugly hacks. Look for 'RR'. 118 ;; Version 0.2 added features 119 ;; 11/11/2002: added preview support. 120 ;; 10/11/2002: indent a graph or subgraph at once with C-M-q. 121 ;; 08/11/2002: relaxed rules for indentation, the may now be extra chars 122 ;; after beginning of graph (comment's for example). 123 ;; Version 0.1.2 bug fixes and naming issues 124 ;; 06/11/2002: renamed dot-font-lock-defaults to dot-font-lock-keywords. 125 ;; added some documentation to dot-colors. 126 ;; provided a much better way to handle my max-specpdl-size 127 ;; problem. 128 ;; added an extra autoload cookie (hope this helps, as I don't 129 ;; yet use autoload myself) 130 ;; Version 0.1.1 bug fixes 131 ;; 06/11/2002: added an missing attribute, for font-locking to work. 132 ;; fixed the regex generating, so that it only recognizes 133 ;; whole words 134 ;; 05/11/2002: there can now be extra whitespace chars after an '{'. 135 ;; 04/11/2002: Why I use max-specpdl-size is now documented, and old value 136 ;; gets restored. 137 ;; Version 0.1 initial release 138 ;; 02/11/2002: implemented parser for *compilation* of a .dot file. 139 ;; 01/11/2002: implemented compilation of an .dot file. 140 ;; 31/10/2002: added syntax-table to the mode. 141 ;; 30/10/2002: implemented indentation code. 142 ;; 29/10/2002: implemented all of font-lock. 143 ;; 28/10/2002: derived graphviz-dot-mode from fundamental-mode, started 144 ;; implementing font-lock. 145 146 ;;; Code: 147 148 (require 'compile) 149 (require 'subr-x) 150 151 (defconst graphviz-dot-mode-version "0.4.1" 152 "Version of `graphviz-dot-mode.el'.") 153 154 (defgroup graphviz nil 155 "Major mode for editing Graphviz Dot files" 156 :group 'tools) 157 158 (defun graphviz-dot-customize () 159 "Run \\[customize-group] for the `graphviz' group." 160 (interactive) 161 (customize-group 'graphviz)) 162 163 (defvar graphviz-dot-mode-abbrev-table nil 164 "Abbrev table in use in Graphviz Dot mode buffers.") 165 (define-abbrev-table 'graphviz-dot-mode-abbrev-table ()) 166 167 (defcustom graphviz-dot-dot-program "dot" 168 "*Location of the dot program. This is used by `compile'." 169 :type 'string 170 :group 'graphviz) 171 172 (defcustom graphviz-dot-layout-programs 173 '("dot" "neato" "fdp" "sfdp" "twopi" "twopi" "circo") 174 "*List of layout programs for the user to choose from." 175 :type 'list 176 :group 'graphviz) 177 178 (defcustom graphviz-dot-view-command "dotty %s" 179 "*External program to run on the buffer. 180 You can use `%s' in this string, and it will be substituted by the buffer name." 181 :type 'string 182 :group 'graphviz) 183 184 (defcustom graphviz-dot-view-edit-command nil 185 "*Whether to allow the user to edit the command to run an external viewer." 186 :type 'boolean 187 :group 'graphviz) 188 189 (defcustom graphviz-dot-save-before-view t 190 "*If not nil, \\[graphviz-dot-view] saves the current buffer before running the command." 191 :type 'boolean 192 :group 'graphviz) 193 194 (defcustom graphviz-dot-indent-width tab-width 195 "*Indentation width in Graphviz Dot mode buffers." 196 :type 'integer 197 :group 'graphviz) 198 199 (defcustom graphviz-dot-preview-extension "png" 200 "*The extension to use for the compilation and preview commands. 201 The default format for the compilation command is `dot -T png 202 file.dot -o file.png'." 203 :type 'string 204 :group 'graphviz) 205 206 (defcustom graphviz-dot-auto-preview-on-save nil 207 "*Determines if saving the buffer should automatically trigger preview." 208 :type 'boolean 209 :group 'graphviz) 210 211 (defcustom graphviz-dot-revert-delay 300 212 "*Amount of time to sleep before attempting to display the rendered image." 213 :type 'number 214 :group 'graphviz) 215 216 (defcustom graphviz-dot-attr-keywords 217 '("graph" "digraph" "subgraph" "node" "edge" "strict" "rankdir" 218 "size" "page" "Damping" "Epsilon" "URL" "arrowhead" "arrowsize" 219 "arrowtail" "bb" "bgcolor" "bottomlabel" "center" "clusterrank" 220 "color" "colorscheme" "comment" "compound" 221 "concentrate" "constraint" "decorate" 222 "dim" "dir" "distortion" "fillcolor" "fixedsize" "fontcolor" 223 "fontname" "fontpath" "fontsize" "group" "headURL" "headlabel" 224 "headport" "height" "label" "labelangle" "labeldistance" "labelfloat" 225 "labelfontcolor" "labelfontname" "labelfontsize" "labeljust" 226 "labelloc" "layer" "layers" "len" "lhead" "lp" "ltail" "margin" 227 "maxiter" "mclimit" "minlen" "model" "nodesep" "normalize" "nslimit" 228 "nslimit1" "ordering" "orientation" "overlap" "pack" "pagedir" 229 "pencolor" "peripheries" "pin" "pos" "quantum" "rank" "ranksep" 230 "ratio" "rects" "regular" "remincross" "rotate" "samehead" "sametail" 231 "samplepoint" "searchsize" "sep" "shape" "shapefile" "showboxes" 232 "sides" "skew" "splines" "start" "style" "stylesheet" "tailURL" 233 "taillabel" "tailport" "toplabel" "vertices" "voro_margin" "weight" 234 "z" "width" "penwidth" "mindist" "scale" "patch" "root") 235 "*Keywords for attribute names in a graph. 236 This is used by the auto completion code. The actual completion 237 tables are built when the mode is loaded, so changes to this are 238 not immediately visible." 239 :type '(repeat (string :tag "Keyword")) 240 :group 'graphviz) 241 242 (defvar graphviz-attributes-type-arrow 243 '("arrowhead" "arrowtail") 244 "The attributes that are of type `arrow'. 245 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 246 more information about possible attributes.") 247 248 (defvar graphviz-values-type-arrow 249 '("box" "lbox" "rbox" "obox" "olbox" "orbox" 250 "crow" "lcrow" "rcrow" 251 "diamond" "ldiamond" "rdiamond" "odiamond" "oldiamond" "ordiamond" 252 "dot" "odot" 253 "inv" "linv" "rinv" "oinv" "olinv" "orinv" 254 "none" 255 "normal" "lnormal" "rnormal" "onormal" "olnormal" "ornormal" 256 "tee" "ltee" "rtee" 257 "vee" "lvee" "rvee" 258 "curve" "lcurve" "rcurve" "ocurve" "olcurve" "orcurve") 259 "The possible values that an attribute of type `arrow' can have. 260 See https://graphviz.gitlab.io/_pages/doc/info/arrows.html for 261 more information about the arrow shape.") 262 263 (defvar graphviz-attributes-type-shape 264 '("shape") 265 "The attributes that are of type `shape'. 266 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 267 more information about possible attributes.") 268 269 (defvar graphviz-values-type-shape 270 '("box" "polygon" "ellipse" "oval" "circle" "point" "egg" "triangle" "plaintext" 271 "plain" "diamond" "trapezium" "parallelogram" "house" "pentagon" "hexagon" 272 "septagon" "octagon" "doublecircle" "doubleoctagon" "tripleoctagon" 273 "invtriangle" "invtrapezium" "invhouse" "Mdiamond" "Msquare" "Mcircle" "rect" 274 "rectangle" "square" "star" "none" "underline" "cylinder" "note" "tab" "folder" 275 "box3d" "component" "promoter" "cds" "terminator" "utr" "primersite" 276 "restrictionsite" "fivepoverhang" "threepoverhang" "noverhang" "assembly" 277 "signature" "insulator" "ribosite" "rnastab" "proteasesite" "proteinstab" 278 "rpromoter" "rarrow" "larrow" "lpromoter") 279 "The possible values that an attribute of type `shape' can have. 280 See https://graphviz.gitlab.io/_pages/doc/info/shape.html for 281 more information about the node shapes.") 282 283 (defvar graphviz-attributes-type-style 284 '("style") 285 "The attributes that are of type `style'. 286 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 287 more information about possible attributes.") 288 289 (defvar graphviz-values-type-style 290 '("dashed" "dotted" "solid" "invis" "bold" "tapered" "filled" "striped" 291 "wedged" "diagonals" "rounded" "filled" "striped" "rounded" "radial") 292 "The possible values that an attribute of type `style' can have. 293 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:style for 294 more information about possible styles.") 295 296 (defvar graphviz-attributes-type-dir 297 '("dir") 298 "The attributes that are of type `bool'. 299 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 300 more information about possible attributes.") 301 302 (defvar graphviz-values-type-dir 303 '("forward" "back" "both" "none") 304 "The possible values that an attribute of type `dir' can have. 305 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:dirType for 306 more information about the direction that edges can have.") 307 308 (defvar graphviz-attributes-type-outputmode 309 '("outputorder") 310 "The attributes that are of type `outputMode'. 311 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 312 more information about possible attributes.") 313 314 (defvar graphviz-values-type-outputmode 315 '("breadthfirst" "nodesfirst" "edgesfirst") 316 "The possible values that an attribute of type `outputMode' can have. 317 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:outputMode for 318 more information.") 319 320 (defvar graphviz-attributes-type-packmode 321 '("packmode") 322 "The attributes that are of type `packMode'. 323 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 324 more information about possible attributes.") 325 326 (defvar graphviz-values-type-packmode 327 '("node" "clust" "array") 328 "The possible values that an attribute of type `packMode' can have. 329 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:packMode for 330 more information.") 331 332 (defvar graphviz-attributes-type-pagedir 333 '("pagedir") 334 "The attributes that are of type `pagedir'. 335 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 336 more information about possible attributes.") 337 338 (defvar graphviz-values-type-pagedir 339 '("BL" "BR" "TL" "TR" "RB" "RT" "LB" "LT") 340 "The possible values that an attribute of type `pagedir' can have. 341 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:pagedir for 342 more information.") 343 344 (defvar graphviz-attributes-type-bool 345 '("center" "compound" "concentrate" "constraint" "decorate" 346 "diredgeconstraints" "fixedsize" "forcelabels" "headclip" "imagescale" 347 "labelfloat" "landscape" "mosek" "newrank" "nojustify" "normalize" 348 "notranslate" "overlap" "overlap_shrink" "pack" "pin" "quadtree" "regular" 349 "remincross" "root" "splines" "tailclip" "truecolor") 350 "The attributes that are of type `bool'. 351 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 352 more information about possible attributes.") 353 354 (defvar graphviz-values-type-bool 355 '("true" "false" "yes" "no" "1" "0") 356 "The possible values that an attribute of type `bool' can have.") 357 358 (defvar graphviz-attributes-type-portpos 359 '("headport" "tailport") 360 "The attributes that are of type `portPos'. 361 See https://graphviz.gitlab.io/_pages/doc/info/attrs.html for 362 more information about possible attributes.") 363 364 (defvar graphviz-values-type-portpos 365 '("n" "ne" "e" "se" "s" "sw" "w" "nw" "c" "_") 366 "The possible values that an attribute of type `portPos' can have. 367 The can also be used on the edge as a compass point. See 368 https://graphviz.gitlab.io/_pages/doc/info/attrs.html#k:portPos 369 for more information.") 370 371 (defcustom graphviz-dot-value-keywords 372 '("true" "false" "normal" "inv" "dot" "invdot" "odot" "invodot" 373 "none" "tee" "empty" "invempty" "diamond" "odiamond" "box" "obox" 374 "open" "crow" "halfopen" "local" "global" "none" "forward" "back" 375 "both" "none" "BL" "BR" "TL" "TR" "RB" "RT" "LB" "LT" ":n" ":ne" ":e" 376 ":se" ":s" ":sw" ":w" ":nw" "same" "min" "source" "max" "sink" "LR" 377 "box" "polygon" "ellipse" "circle" "point" "egg" "triangle" 378 "plaintext" "diamond" "trapezium" "parallelogram" "house" "hexagon" 379 "octagon" "doublecircle" "doubleoctagon" "tripleoctagon" "invtriangle" 380 "invtrapezium" "invhouse" "Mdiamond" "Msquare" "Mcircle" "record" 381 "Mrecord" "dashed" "dotted" "solid" "invis" "bold" "filled" 382 "diagonals" "rounded" ) 383 "*Keywords for attribute values. 384 This is used by the auto completion code. The actual completion 385 tables are built when the mode is loaded, so changes to this are 386 not immediately visible." 387 :type '(repeat (string :tag "Keyword")) 388 :group 'graphviz) 389 390 ;;; Font-locking: 391 (defvar graphviz-dot-color-keywords 392 '("aliceblue" "antiquewhite" "antiquewhite1" "antiquewhite2" 393 "antiquewhite3" "antiquewhite4" "aquamarine" "aquamarine1" 394 "aquamarine2" "aquamarine3" "aquamarine4" "azure" "azure1" 395 "azure2" "azure3" "azure4" "beige" "bisque" "bisque1" "bisque2" 396 "bisque3" "bisque4" "black" "blanchedalmond" "blue" "blue1" 397 "blue2" "blue3" "blue4" "blueviolet" "brown" "brown1" "brown2" 398 "brown3" "brown4" "burlywood" "burlywood1" "burlywood2" 399 "burlywood3" "burlywood4" "cadetblue" "cadetblue1" 400 "cadetblue2" "cadetblue3" "cadetblue4" "chartreuse" 401 "chartreuse1" "chartreuse2" "chartreuse3" "chartreuse4" 402 "chocolate" "chocolate1" "chocolate2" "chocolate3" "chocolate4" 403 "coral" "coral1" "coral2" "coral3" "coral4" "cornflowerblue" 404 "cornsilk" "cornsilk1" "cornsilk2" "cornsilk3" "cornsilk4" 405 "crimson" "cyan" "cyan1" "cyan2" "cyan3" "cyan4" "darkgoldenrod" 406 "darkgoldenrod1" "darkgoldenrod2" "darkgoldenrod3" 407 "darkgoldenrod4" "darkgreen" "darkkhaki" "darkolivegreen" 408 "darkolivegreen1" "darkolivegreen2" "darkolivegreen3" 409 "darkolivegreen4" "darkorange" "darkorange1" "darkorange2" 410 "darkorange3" "darkorange4" "darkorchid" "darkorchid1" 411 "darkorchid2" "darkorchid3" "darkorchid4" "darksalmon" 412 "darkseagreen" "darkseagreen1" "darkseagreen2" 413 "darkseagreen3" "darkseagreen4" "darkslateblue" 414 "darkslategray" "darkslategray1" "darkslategray2" 415 "darkslategray3" "darkslategray4" "darkslategrey" 416 "darkturquoise" "darkviolet" "deeppink" "deeppink1" 417 "deeppink2" "deeppink3" "deeppink4" "deepskyblue" 418 "deepskyblue1" "deepskyblue2" "deepskyblue3" "deepskyblue4" 419 "dimgray" "dimgrey" "dodgerblue" "dodgerblue1" "dodgerblue2" 420 "dodgerblue3" "dodgerblue4" "firebrick" "firebrick1" 421 "firebrick2" "firebrick3" "firebrick4" "floralwhite" 422 "forestgreen" "gainsboro" "ghostwhite" "gold" "gold1" "gold2" 423 "gold3" "gold4" "goldenrod" "goldenrod1" "goldenrod2" 424 "goldenrod3" "goldenrod4" "gray" "gray0" "gray1" "gray10" "gray100" 425 "gray11" "gray12" "gray13" "gray14" "gray15" "gray16" "gray17" 426 "gray18" "gray19" "gray2" "gray20" "gray21" "gray22" "gray23" 427 "gray24" "gray25" "gray26" "gray27" "gray28" "gray29" "gray3" 428 "gray30" "gray31" "gray32" "gray33" "gray34" "gray35" "gray36" 429 "gray37" "gray38" "gray39" "gray4" "gray40" "gray41" "gray42" 430 "gray43" "gray44" "gray45" "gray46" "gray47" "gray48" "gray49" 431 "gray5" "gray50" "gray51" "gray52" "gray53" "gray54" "gray55" 432 "gray56" "gray57" "gray58" "gray59" "gray6" "gray60" "gray61" 433 "gray62" "gray63" "gray64" "gray65" "gray66" "gray67" "gray68" 434 "gray69" "gray7" "gray70" "gray71" "gray72" "gray73" "gray74" 435 "gray75" "gray76" "gray77" "gray78" "gray79" "gray8" "gray80" 436 "gray81" "gray82" "gray83" "gray84" "gray85" "gray86" "gray87" 437 "gray88" "gray89" "gray9" "gray90" "gray91" "gray92" "gray93" 438 "gray94" "gray95" "gray96" "gray97" "gray98" "gray99" "green" 439 "green1" "green2" "green3" "green4" "greenyellow" "grey" "grey0" 440 "grey1" "grey10" "grey100" "grey11" "grey12" "grey13" "grey14" 441 "grey15" "grey16" "grey17" "grey18" "grey19" "grey2" "grey20" 442 "grey21" "grey22" "grey23" "grey24" "grey25" "grey26" "grey27" 443 "grey28" "grey29" "grey3" "grey30" "grey31" "grey32" "grey33" 444 "grey34" "grey35" "grey36" "grey37" "grey38" "grey39" "grey4" 445 "grey40" "grey41" "grey42" "grey43" "grey44" "grey45" "grey46" 446 "grey47" "grey48" "grey49" "grey5" "grey50" "grey51" "grey52" 447 "grey53" "grey54" "grey55" "grey56" "grey57" "grey58" "grey59" 448 "grey6" "grey60" "grey61" "grey62" "grey63" "grey64" "grey65" 449 "grey66" "grey67" "grey68" "grey69" "grey7" "grey70" "grey71" 450 "grey72" "grey73" "grey74" "grey75" "grey76" "grey77" "grey78" 451 "grey79" "grey8" "grey80" "grey81" "grey82" "grey83" "grey84" 452 "grey85" "grey86" "grey87" "grey88" "grey89" "grey9" "grey90" 453 "grey91" "grey92" "grey93" "grey94" "grey95" "grey96" "grey97" 454 "grey98" "grey99" "honeydew" "honeydew1" "honeydew2" "honeydew3" 455 "honeydew4" "hotpink" "hotpink1" "hotpink2" "hotpink3" "hotpink4" 456 "indianred" "indianred1" "indianred2" "indianred3" "indianred4" 457 "indigo" "ivory" "ivory1" "ivory2" "ivory3" "ivory4" "khaki" "khaki1" 458 "khaki2" "khaki3" "khaki4" "lavender" "lavenderblush" 459 "lavenderblush1" "lavenderblush2" "lavenderblush3" 460 "lavenderblush4" "lawngreen" "lemonchiffon" "lemonchiffon1" 461 "lemonchiffon2" "lemonchiffon3" "lemonchiffon4" "lightblue" 462 "lightblue1" "lightblue2" "lightblue3" "lightblue4" 463 "lightcoral" "lightcyan" "lightcyan1" "lightcyan2" "lightcyan3" 464 "lightcyan4" "lightgoldenrod" "lightgoldenrod1" 465 "lightgoldenrod2" "lightgoldenrod3" "lightgoldenrod4" 466 "lightgoldenrodyellow" "lightgray" "lightgrey" "lightpink" 467 "lightpink1" "lightpink2" "lightpink3" "lightpink4" 468 "lightsalmon" "lightsalmon1" "lightsalmon2" "lightsalmon3" 469 "lightsalmon4" "lightseagreen" "lightskyblue" "lightskyblue1" 470 "lightskyblue2" "lightskyblue3" "lightskyblue4" 471 "lightslateblue" "lightslategray" "lightslategrey" 472 "lightsteelblue" "lightsteelblue1" "lightsteelblue2" 473 "lightsteelblue3" "lightsteelblue4" "lightyellow" 474 "lightyellow1" "lightyellow2" "lightyellow3" "lightyellow4" 475 "limegreen" "linen" "magenta" "magenta1" "magenta2" "magenta3" 476 "magenta4" "maroon" "maroon1" "maroon2" "maroon3" "maroon4" 477 "mediumaquamarine" "mediumblue" "mediumorchid" 478 "mediumorchid1" "mediumorchid2" "mediumorchid3" 479 "mediumorchid4" "mediumpurple" "mediumpurple1" 480 "mediumpurple2" "mediumpurple3" "mediumpurple4" 481 "mediumseagreen" "mediumslateblue" "mediumspringgreen" 482 "mediumturquoise" "mediumvioletred" "midnightblue" 483 "mintcream" "mistyrose" "mistyrose1" "mistyrose2" "mistyrose3" 484 "mistyrose4" "moccasin" "navajowhite" "navajowhite1" 485 "navajowhite2" "navajowhite3" "navajowhite4" "navy" "navyblue" 486 "oldlace" "olivedrab" "olivedrap" "olivedrab1" "olivedrab2" 487 "olivedrap3" "oragne" "palegoldenrod" "palegreen" "palegreen1" 488 "palegreen2" "palegreen3" "palegreen4" "paleturquoise" 489 "paleturquoise1" "paleturquoise2" "paleturquoise3" 490 "paleturquoise4" "palevioletred" "palevioletred1" 491 "palevioletred2" "palevioletred3" "palevioletred4" 492 "papayawhip" "peachpuff" "peachpuff1" "peachpuff2" 493 "peachpuff3" "peachpuff4" "peru" "pink" "pink1" "pink2" "pink3" 494 "pink4" "plum" "plum1" "plum2" "plum3" "plum4" "powderblue" 495 "purple" "purple1" "purple2" "purple3" "purple4" "red" "red1" "red2" 496 "red3" "red4" "rosybrown" "rosybrown1" "rosybrown2" "rosybrown3" 497 "rosybrown4" "royalblue" "royalblue1" "royalblue2" "royalblue3" 498 "royalblue4" "saddlebrown" "salmon" "salmon1" "salmon2" "salmon3" 499 "salmon4" "sandybrown" "seagreen" "seagreen1" "seagreen2" 500 "seagreen3" "seagreen4" "seashell" "seashell1" "seashell2" 501 "seashell3" "seashell4" "sienna" "sienna1" "sienna2" "sienna3" 502 "sienna4" "skyblue" "skyblue1" "skyblue2" "skyblue3" "skyblue4" 503 "slateblue" "slateblue1" "slateblue2" "slateblue3" "slateblue4" 504 "slategray" "slategray1" "slategray2" "slategray3" "slategray4" 505 "slategrey" "snow" "snow1" "snow2" "snow3" "snow4" "springgreen" 506 "springgreen1" "springgreen2" "springgreen3" "springgreen4" 507 "steelblue" "steelblue1" "steelblue2" "steelblue3" "steelblue4" 508 "tan" "tan1" "tan2" "tan3" "tan4" "thistle" "thistle1" "thistle2" 509 "thistle3" "thistle4" "tomato" "tomato1" "tomato2" "tomato3" 510 "tomato4" "transparent" "turquoise" "turquoise1" "turquoise2" 511 "turquoise3" "turquoise4" "violet" "violetred" "violetred1" 512 "violetred2" "violetred3" "violetred4" "wheat" "wheat1" "wheat2" 513 "wheat3" "wheat4" "white" "whitesmoke" "yellow" "yellow1" "yellow2" 514 "yellow3" "yellow4" "yellowgreen") 515 "Possible color constants in the dot language. 516 The list of constant is available at http://www.research.att.com/~erg/graphviz\ 517 /info/colors.html") 518 519 520 ;;; Key map 521 (defvar graphviz-dot-mode-map 522 (let ((map (make-sparse-keymap))) 523 (define-key map "\C-\M-q" 'graphviz-dot-indent-graph) 524 (define-key map "\C-c\C-p" 'graphviz-dot-preview) 525 (define-key map "\C-c\C-c" 'compile) 526 (define-key map "\C-c\C-v" 'graphviz-dot-view) 527 map) 528 "Keymap used in Graphviz Dot mode.") 529 530 ;;; Syntax table 531 (defvar graphviz-dot-mode-syntax-table 532 (let ((st (make-syntax-table))) 533 (modify-syntax-entry ?/ ". 124b" st) 534 (modify-syntax-entry ?* ". 23" st) 535 (modify-syntax-entry ?\n "> b" st) 536 (modify-syntax-entry ?= "." st) 537 (modify-syntax-entry ?_ "_" st) 538 (modify-syntax-entry ?- "_" st) 539 (modify-syntax-entry ?> "." st) 540 (modify-syntax-entry ?\[ "(]" st) 541 (modify-syntax-entry ?\] ")[" st) 542 (modify-syntax-entry ?\" "\"" st) 543 st) 544 "Syntax table for `graphviz-dot-mode'.") 545 546 (defvar graphviz-dot-syntax-propertize-function 547 (syntax-propertize-rules 548 ("^#" (0 "< b")))) 549 550 (defvar graphviz-dot-font-lock-keywords 551 ;; See https://graphviz.gitlab.io/_pages/doc/info/lang.html. 552 `(;; Match ID, first case 553 ("\\(?:di\\|sub\\)?graph\\(?:[[:space:]]+\\)\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)" 554 (1 font-lock-function-name-face)) 555 ;; Match ID, second case 556 ("\\(?:di\\|sub\\)?graph\\(?:[[:space:]]+\\)\\(-?[0-9]*\\(\\.[0-9]*\\)?\\)" 557 (1 font-lock-function-name-face)) 558 (,(regexp-opt graphviz-dot-value-keywords 'words) 559 . font-lock-reference-face) 560 ;; to build the font-locking for the colors, 561 ;; we need more room for max-specpdl-size, 562 ;; after that we take the list of symbols, 563 ;; convert them to a list of strings, and make 564 ;; an optimized regexp from them 565 (,(let ((max-specpdl-size (max max-specpdl-size 1200))) 566 (regexp-opt graphviz-dot-color-keywords 'words)) 567 . font-lock-string-face) 568 (,(concat 569 (regexp-opt graphviz-dot-attr-keywords 'words) 570 "[ \\t\\n]*=") 571 ;; RR - ugly, really, but I don't know why xemacs does not work 572 ;; if I change the next car to "1"... 573 (0 font-lock-variable-name-face)) 574 ;; The 'graph' nonterminal 575 ("\\(\\_<\\(?:strict\\)?[[:space:]]*\\(?:\\(?:di\\)?graph\\)\\_>\\)" 576 (1 'font-lock-keyword-face)) 577 ;; The 'attr_stmt' 578 ("\\_<\\(edge\\|graph\\|node\\)\\_>[[:space:]]*\\[" 579 1 'font-lock-keyword-face) 580 ;; The 'subgraph' nonterminal 581 ("\\_<subgraph\\_>" . 'font-lock-keyword-face)) 582 "Keyword highlighting specification for `graphviz-dot-mode'.") 583 584 (defun graphviz-output-file-name (f-name) 585 "Return the filename of the preview, using F-NAME." 586 (concat (file-name-sans-extension f-name) 587 "." graphviz-dot-preview-extension)) 588 589 (defun graphviz-compile-command (f-name) 590 "Shell command to compile F-NAME. 591 By default this is `dot -T png file.dot -o file.png', the used 592 program to compile can be changed by setting 593 `graphviz-dot-dot-program', the output format and extension can 594 be changed with `graphviz-dot-preview-extension'." 595 (when f-name 596 (setq compile-command 597 (concat graphviz-dot-dot-program 598 " -T" graphviz-dot-preview-extension " " 599 (shell-quote-argument f-name) 600 " -o " 601 (shell-quote-argument 602 (graphviz-output-file-name f-name)))))) 603 604 (defvar dot-menu nil 605 "Menu for Graphviz Dot Mode. 606 This menu will get created automatically if you have the `easymenu' 607 package.") 608 609 ;;;###autoload 610 (define-derived-mode graphviz-dot-mode prog-mode "dot" 611 "Major mode for the dot language. \\<graphviz-dot-mode-map> 612 TAB indents for graph lines. 613 614 \\[graphviz-dot-indent-graph]\t- Indentation function. 615 \\[graphviz-dot-preview]\t- Previews graph in a buffer. 616 \\[graphviz-dot-view]\t- Views graph in an external viewer. 617 \\[graphviz-dot-indent-line]\t- Indents current line of code. 618 619 Variables specific to this mode: 620 621 `graphviz-dot-dot-program' (default `dot') 622 Program used to compile the graphs. 623 `graphviz-dot-preview-extension' (default `png') 624 File type to use for output. 625 `graphviz-dot-view-command' (default `dotty %s') 626 Command to run when `graphviz-dot-view' is executed. 627 `graphviz-dot-view-edit-command' (default nil) 628 If the user should be asked to edit the view command. 629 `graphviz-dot-save-before-view' (default t) 630 Automatically save current buffer berore `graphviz-dot-view'." 631 :group 'graphviz 632 (setq-local font-lock-defaults '(graphviz-dot-font-lock-keywords)) 633 (setq-local comment-start "//") 634 (setq-local comment-start-skip "/\\*+ *\\|//+ *") 635 (setq-local indent-line-function 'graphviz-dot-indent-line) 636 (setq-local syntax-propertize-function 637 graphviz-dot-syntax-propertize-function) 638 (when (buffer-file-name) 639 (setq-local compile-command 640 (graphviz-compile-command (buffer-file-name)))) 641 (when dot-menu (easy-menu-add dot-menu)) 642 (add-to-list 'compilation-error-regexp-alist 'dot) 643 (add-to-list 'compilation-error-regexp-alist-alist 644 '(dot "^Error: \\(.+\\): .*error in line \\([0-9]+\\).*" 1 2)) 645 (add-hook 'after-save-hook 'graphviz-live-reload-hook) 646 (run-hooks 'graphviz-dot-mode-hook)) 647 648 ;;;; Menu definitions 649 650 (and (condition-case nil 651 (require 'easymenu) 652 (error nil)) 653 (easy-menu-define 654 dot-menu graphviz-dot-mode-map "Graphviz Mode menu" 655 '("Graphviz" 656 ["Indent Graph" graphviz-dot-indent-graph t] 657 ["Comment Out Region" comment-region (mark)] 658 ["Uncomment Region" uncomment-region (mark)] 659 "-" 660 ["Compile" compile t] 661 ["Preview" graphviz-dot-preview 662 (and (buffer-file-name) 663 (not (buffer-modified-p)))] 664 ["External Viewer" graphviz-dot-view (buffer-file-name)] 665 "-" 666 ["Customize..." graphviz-dot-customize t] 667 ))) 668 669 ;;;; 670 ;;;; Indentation 671 ;;;; 672 673 (defun graphviz-dot-indent-line () 674 "Indent current line of dot code." 675 (interactive) 676 (if (bolp) 677 (graphviz-dot-real-indent-line) 678 (save-excursion 679 (graphviz-dot-real-indent-line)))) 680 681 (defun graphviz-dot-real-indent-line () 682 "Indent current line of dot code." 683 (beginning-of-line) 684 (cond 685 ((bobp) 686 ;; simple case, indent to 0 687 (indent-line-to 0)) 688 ((looking-at "^[ \t]*}[ \t]*$") 689 ;; block closing, deindent relative to previous line 690 (indent-line-to (save-excursion 691 (forward-line -1) 692 (if (looking-at "\\(^.*{[^}]*$\\)") 693 ;; previous line opened a block 694 ;; use same indentation 695 (current-indentation) 696 (max 0 (- (current-indentation) graphviz-dot-indent-width)))))) 697 ;; other cases need to look at previous lines 698 (t 699 (indent-line-to (save-excursion 700 (forward-line -1) 701 (cond 702 ((looking-at "\\(^.*{[^}]*$\\)") 703 ;; previous line opened a block 704 ;; indent to that line 705 (+ (current-indentation) graphviz-dot-indent-width)) 706 ((and (not (looking-at ".*\\[.*\\].*")) 707 (looking-at ".*\\[.*")) ; TODO:PP : can be 1 regex 708 ;; previous line started filling 709 ;; attributes, intend to that start 710 (search-forward "[") 711 (current-column)) 712 ((and (not (looking-at ".*\\[.*\\].*")) 713 (looking-at ".*\\].*")) ; TODO:PP : " 714 ;; previous line stopped filling 715 ;; attributes, find the line that started 716 ;; filling them and indent to that line 717 (while (or (looking-at ".*\\[.*\\].*") 718 (not (looking-at ".*\\[.*"))) ; TODO:PP : " 719 (forward-line -1)) 720 (current-indentation)) 721 (t 722 ;; default case, indent the 723 ;; same as previous NON-BLANK line 724 ;; (or the first line, if there are no previous non-blank lines) 725 (while (and (< (point-min) (point)) 726 (looking-at "^\[ \t\]*$")) 727 (forward-line -1)) 728 ;; if we find a closing square bracket, don't indent 729 ;; to the level of its attributes, but instead 730 ;; find the opening bracket and indent to that 731 (if (looking-at ".*\\].*") 732 (while (not (looking-at ".*\\[.*")) 733 (forward-line -1))) 734 (current-indentation)) ))) ))) 735 736 (defun graphviz-dot-indent-graph () 737 "Indent the graph/digraph/subgraph where point is at. 738 This will first teach the beginning of the graph were point is at, and 739 then indent this and each subgraph in it." 740 (interactive) 741 (save-excursion 742 ;; position point at start of graph 743 (while (not (or (looking-at "\\(^.*{[^}]*$\\)") (bobp))) 744 (forward-line -1)) 745 ;; bracket { one +; bracket } one - 746 (let ((bracket-count 0)) 747 (while 748 (progn 749 (cond 750 ;; update bracket-count 751 ((looking-at "\\(^.*{[^}]*$\\)") 752 (setq bracket-count (+ bracket-count 1))) 753 ;; update bracket-count 754 ((looking-at "^[ \t]*}[ \t]*$") 755 (setq bracket-count (- bracket-count 1)))) 756 ;; indent this line and move on 757 (graphviz-dot-indent-line) 758 (forward-line 1) 759 ;; as long as we are not completed or at end of buffer 760 (and (> bracket-count 0) (not (eobp)))))))) 761 762 ;;;###autoload 763 (defun graphviz-dot-preview () 764 "Compile the graph and preview it in an other buffer." 765 (interactive) 766 (save-buffer) 767 (let ((windows (window-list)) 768 (f-name (graphviz-output-file-name (buffer-file-name))) 769 (command-result (string-trim (shell-command-to-string compile-command)))) 770 (if (string-prefix-p "Error:" command-result) 771 (message command-result) 772 (progn 773 (sleep-for 0 graphviz-dot-revert-delay) 774 (when (= (length windows) 1) 775 (split-window-sensibly)) 776 (with-selected-window (selected-window) 777 (switch-to-buffer-other-window (find-file-noselect f-name t)) 778 ;; I get "changed on disk; really edit the buffer?" prompt w/o this 779 (sleep-for 0 50) 780 (revert-buffer t t)))))) 781 782 ;;;###autoload 783 (defun graphviz-turn-on-live-preview () 784 "Turn on live preview. 785 This will update the preview on every save." 786 (interactive) 787 (setq graphviz-dot-auto-preview-on-save t) 788 (add-hook 'after-save-hook 'graphviz-live-reload-hook)) 789 790 ;;;###autoload 791 (defun graphviz-turn-off-live-preview () 792 "Turn off live preview. 793 Saving the file will no longer also update the preview." 794 (interactive) 795 (setq graphviz-dot-auto-preview-on-save nil) 796 (remove-hook 'after-save-hook 'graphviz-live-reload-hook)) 797 798 (defun graphviz-live-reload-hook () 799 "Hook to run in `after-save-hook' for live preview to work." 800 (when (and (eq major-mode 'graphviz-dot-mode) graphviz-dot-auto-preview-on-save) 801 (graphviz-dot-preview))) 802 803 ;;;; 804 ;;;; View 805 ;;;; 806 (defun graphviz-dot-view () 807 "Run an external viewer. 808 This creates an external process every time it is executed. If 809 `graphviz-dot-save-before-view' is set, the current buffer is 810 saved before the command is executed." 811 (interactive) 812 (let ((cmd (if graphviz-dot-view-edit-command 813 (if (fboundp 'read-shell-command) 814 (read-shell-command "View command: " 815 (format graphviz-dot-view-command 816 (shell-quote-argument (buffer-file-name)))) 817 ;; read-shell-command not available in GNU Emacs 21 818 (read-from-minibuffer "View command: " 819 (format graphviz-dot-view-command 820 (shell-quote-argument (buffer-file-name))))) 821 (format graphviz-dot-view-command 822 (shell-quote-argument (buffer-file-name)))))) 823 (if graphviz-dot-save-before-view 824 (save-buffer)) 825 (start-process-shell-command (downcase mode-name) nil cmd) 826 (message (format "Executing `%s'..." cmd)))) 827 828 (defun graphviz-dot-set-layout () 829 "Change the value of `graphviz-dot-dot-program'." 830 (interactive) 831 (setq graphviz-dot-dot-program 832 (completing-read "Layout: " graphviz-dot-layout-programs))) 833 834 ;;;###autoload 835 (add-to-list 'auto-mode-alist '("\\.dot\\'" . graphviz-dot-mode)) 836 ;;;###autoload 837 (add-to-list 'auto-mode-alist '("\\.gv\\'" . graphviz-dot-mode)) 838 839 ;; Support org-mode, when adding a code block for dot, use this mode 840 (with-eval-after-load 'org-src 841 (defvar org-src-lang-modes) 842 (add-to-list 'org-src-lang-modes '("dot" . graphviz-dot))) 843 844 (provide 'graphviz-dot-mode) 845 ;;; graphviz-dot-mode.el ends here