;;; jgraph-mode.el --- Major mode for Jgraph files -*- lexical-binding:t -*- ;; Copyright (C) 2006, 2011-2012, 2014, 2015 Free Software Foundation, Inc ;; Author: Stefan Monnier ;; Version: 1.1 ;; Package-Requires: ((cl-lib "0.5")) ;; Keywords: tex, wp ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;; Simple editing mode for the Jgraph graph plotting tool. ;; You can find more info at http://web.eecs.utk.edu/~plank/plank/jgraph/jgraph.html ;;; Code: (eval-when-compile (require 'cl-lib)) (defgroup jgraph-mode () "Major mode for Jgraph files." :group 'tools) (defvar jgraph-mode-map (let ((map (make-sparse-keymap))) map) "Keymap for `jgraph-mode'.") ;; (easy-menu-define jgraph-mode-menu jgraph-mode-map ;; "Menu for `jgraph-mode'." ;; '("Jgraph" ;; ["Do some stuff" jgraph-do-bar :active mark-active] ;; "..." ;; ["Do some other" jgraph-do-foo] ;; ("Sub-jgraph" ;; ["Haha" jgraph-crash]) ;; ...)) (defvar jgraph-mode-syntax-table (let ((st (make-syntax-table))) ;; tokens are made of any non-whitespace chars. (map-char-table (lambda (c v) (if (not (memq (car v) '(0 2 3))) (modify-syntax-entry c "_" st))) st) (modify-syntax-entry ?\( "_ 1n" st) (modify-syntax-entry ?\) "_ 4n" st) (modify-syntax-entry ?\* "_ 23" st) st) "Syntax table for `jgraph-mode'.") (defun jgraph-extract-commands () (let ((sections nil) (tok-cmds nil)) (goto-char (point-min)) (while (and (re-search-forward "^\\.RE" nil t) (re-search-forward "^\\.B \\(.*\\)" nil t)) (let ((section (downcase (match-string 1))) (commands nil) (end (save-excursion (and (re-search-forward "^\\.RE" nil t) (match-beginning 0))))) (setq section (replace-regexp-in-string "simple" "" section)) (setq section (replace-regexp-in-string "advanced" "" section)) (setq section (replace-regexp-in-string "editing" "" section)) (setq section (replace-regexp-in-string "commands" "" section)) (setq section (replace-regexp-in-string "\\\\" "" section)) (setq section (replace-regexp-in-string "\\` *" "" section)) (setq section (replace-regexp-in-string " *\\'" "" section)) (setq section (replace-regexp-in-string " " "-" section)) (setq section (intern section)) (while (re-search-forward "^\\.TP\n\\(?:\\.B \\|\\\\fB\\)\\([^ :\t\n\\]+\\)\\([ {\\fI]*token\\)?" end 'move) (let ((cmd (match-string-no-properties 1))) (if (match-end 2) (cl-pushnew cmd tok-cmds :test #'equal)) (cl-pushnew cmd commands :test #'equal))) (setq commands (nreverse commands)) (let ((sec (assoc section sections))) (if sec (nconc sec commands) (push (cons section commands) sections))))) (nreverse (cons (cons 'token tok-cmds) sections)))) (defconst jgraph-commands ;; Obtained by running (jgraph-extract-commands) in the jgraph.1 nroff doc. '((top-level-description "newgraph" "graph" "copygraph" "newpage" "X" "Y" "bbox" "preamble" "epilogue") (graph "xaxis" "yaxis" "newcurve" "curve" "newline" "copycurve" "title" "legend" "newstring" "string" "copystring" "border" "noborder" "clip" "noclip" "inherit_axes" "x_translate" "y_translate" "X" "Y") (axis "linear" "log" "min" "max" "size" "log_base" "hash" "shash" "mhash" "precision" "hash_format" "label" "draw_at" "nodraw" "draw" "grid_lines" "no_grid_lines" "mgrid_lines" "no_mgrid_lines" "gray" "color" "grid_gray" "grid_color" "mgrid_gray" "mgrid_color" "hash_at" "mhash_at" "hash_label" "hash_labels" "hash_scale" "draw_hash_marks_at" "draw_hash_labels_at" "auto_hash_marks" "no_auto_hash_marks" "auto_hash_labels" "no_auto_hash_labels" "draw_axis" "no_draw_axis" "draw_axis_label" "no_draw_axis_label" "draw_hash_marks" "no_draw_hash_marks" "draw_hash_labels" "no_draw_hash_labels") (curve "pts" "x_epts" "y_epts" "marktype" "marksize" "mrotate" "gray" "color" "fill" "cfill" "pattern" "poly" "nopoly" "pfill" "pcfill" "ppattern" "gmarks" "postscript" "eps" "larrows" "rarrows" "nolarrows" "norarrows" "larrow" "rarrow" "nolarrow" "norarrow" "asize" "afill" "apattern" "linetype" "glines" "linethickness" "bezier" "nobezier" "clip" "noclip" "label") (label "x" "y" "font" "fontsize" "linesep" "hjl" "hjc" "hjr" "vjt" "vjc" "vjb" "rotate" "lgray" "lcolor") (legend "on" "off" "linelength" "linebreak" "midspace" "defaults" "left" "top" "bottom" "x" "y" "custom") (hash-label "at") ;; Commands that can be followed by a token. (token "apattern" "eps" "postscript" "ppattern" "pattern" "hash_format" "epilogue" "preamble"))) (defconst jgraph-file-include-commands '("include" "preamble" "epilogue" "postscript" "eps")) (defvar jgraph-font-lock-keywords `((,(concat "\\_<" (regexp-opt jgraph-file-include-commands) "[ \t]+\\(\\sw+\\)") (1 font-lock-constant-face)) (,(concat "^" (regexp-opt (cdr (assoc 'top-level-description jgraph-commands))) "\\_>") . font-lock-function-name-face) (,(concat "\\_<" (regexp-opt (cons "include" (apply 'append (mapcar 'cdr jgraph-commands)))) "\\_>") . font-lock-keyword-face) ) "Keyword highlighting specification for `jgraph-mode'.") ;; (defvar jgraph-imenu-generic-expression ;; '(("Vars" "^defvar[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1) ;; (nil "^function[ \t]+\\(\\(\\sw\\|\\s_\\)+\\)" 1) ;; ...) ;; "Regex patterns for the index menu of `jgraph-mode'.") ;; (defvar jgraph-outline-regexp "(\\|;;;+" ;; "Regexp for `outline-minor-mode' in `jgraph-mode'.") ;; Abbreviations and Skeletons ;; (define-skeleton jgraph-insert-if ;; "Jgraph mode skeleton for if..then expressions." ;; nil ;; "if " _ \n "then " _ \n "else " _ \n "fi" \n) ;; (define-skeleton jgraph-insert-begend ;; "Jgraph mode skeleton for begin...end expressions." ;; "Block name: " ;; "begin<" str ">" \n _ \n "end<" str ">" \n) ;; (define-abbrev-table 'jgraph-mode-abbrev-table ;; '(("if" "" jgraph-insert-if nil t) ;; ("cwcc" "call-with-current-continuation" nil nil t) ;; ("begin" "" jgraph-insert-begend nil t) ;; )) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.jgr\\'" . jgraph-mode)) (defun jgraph--syntax-end-of-string (limit) (when (eq t (nth 3 (syntax-ppss))) (when (re-search-forward "\\(?:\\=\\|[^\\]\\)\\(\n\\)" limit t) (put-text-property (match-beginning 1) (match-end 1) 'syntax-table (string-to-syntax "|"))))) (defun jgraph--syntax-propertize (start end) (goto-char start) (jgraph--syntax-end-of-string end) (funcall (syntax-propertize-rules ("\\s-:\\(\\s-\\)" (1 (prog1 "|" (jgraph--syntax-end-of-string end))))) start end)) ;;;###autoload (define-derived-mode jgraph-mode prog-mode "Jgraph" "A major mode for editing Jgraph files." (set (make-local-variable 'comment-start) "(* ") (set (make-local-variable 'comment-start-skip) "\\_<(\\*\\_>[ \t]*") (set (make-local-variable 'comment-end) " *)") (set (make-local-variable 'comment-start-skip) "[ \t]*\\_<\\*)\\_>") (set (make-local-variable 'font-lock-defaults) '(jgraph-font-lock-keywords)) (set (make-local-variable 'syntax-propertize-function) #'jgraph--syntax-propertize) (set (make-local-variable 'indent-line-function) 'jgraph-indent-line) ;; (set (make-local-variable 'imenu-generic-expression) ;; jgraph-imenu-generic-expression) ;; (set (make-local-variable 'outline-regexp) jgraph-outline-regexp) ) ;;; Indentation (defcustom jgraph-indent-offset 4 "Basic indentation step size in `jgraph-mode'." :type 'integer) (defun jgraph-indent-line () "Indent current line of Jgraph code." (interactive) (let* ((savep (point)) (indent (or (with-demoted-errors (save-excursion (forward-line 0) (skip-chars-forward " \t") (if (>= (point) savep) (setq savep nil)) (jgraph-indent-calculate))) 'noindent))) (if (not (numberp indent)) indent (setq indent (max indent 0)) (if savep (save-excursion (indent-line-to indent)) (indent-line-to indent))))) (defun jgraph-indent-calculate () "Return the column to which the current line should be indented." (save-excursion (skip-chars-forward " \t") (or (when (looking-at (concat (regexp-opt (cdr (assoc 'top-level-description jgraph-commands))) "\\_>")) 0) 'noindent))) (provide 'jgraph-mode) ;;; jgraph-mode.el ends here