dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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