emacs/layers.personal/mylangs/mymermaid/local/mermaid/mermaid.el
2018-04-07 10:54:04 +08:00

240 lines
8.5 KiB
EmacsLisp

;; mermaid mode
(defvar mermaid-mode-hook nil
"initial hook for mermaid mode"
)
(defun mermaid-compilation-mode-hook ()
"Hook function to set local value for `compilation-error-screen-columns'."
;; In Emacs > 20.7 compilation-error-screen-columns is buffer local.
(or (assq 'compilation-error-screen-columns (buffer-local-variables))
(make-local-variable 'compilation-error-screen-columns))
(setq compilation-error-screen-columns nil))
(defvar mermaid-output-format 'png
"The format of generated file")
(defvar mermaid-verbose nil
"Show verbose information when run compiler")
(defvar mermaid-compiler "mermaid"
"The compiler used to generate output")
(defvar mermaid-mode-map
(let ((map (make-sparse-keymap)))
;; add key bindings for mermaid mode here
;;
(define-key map "\C-j" 'newline-and-indent)
(define-key map "\C-c\C-c" 'mermaid-compile)
(define-key map "\C-c\C-v" 'mermaid-view)
map)
"Keymap for mermaid mode")
(defconst mermaid-font-lock-keywords-1
'(("^[ \t]*\\(graph\\|subgraph\\|end\\|loop\\|alt\\|gantt\\|title\\|section\\|dateFormat\\|sequenceDiagram\\|opt\\|participant\\|note\\|else\\|gitGraph\\|options\\)" . font-lock-keyword-face)
("^[ \t]*graph[ \t]+\\(TD|\\TB\\|BT\\RL\\|LR\\)" . font-lock-keyword-face)
("%%\\(.*$\\)" . font-lock-comment-face)
("{\\(.*\\)}" . font-lock-string-face)
(":\\([^%\012]*\\)[^%\012]*$" . font-lock-warning-face)
)
"keyword in mermaid mode"
)
(defconst mermaid-new-scope-regexp
"^[ \t]*\\(loop\\|opt\\|subgraph\\|graph\\|sequenceDiagram\\|gantt\\|gitGraph\\|{\\)\\([ \t]*\\|$\\)"
"keyword to start a new scope(indent level)")
(defconst mermaid-end-scope-regexp
"^[ \t]*\\(end\\|}\\)\\([ \t]*\\|$\\)"
"keyword for end a scope(maybe also start a new scope)")
(defconst mermaid-section-regexp
"^[ \t]*\\(section\\)[ \t]+"
"section keyword")
(defconst mermaid-else-regexp
"^[ \t]*\\(else\\)"
"else keyword")
(defconst mermaid-alt-regexp
"^[ \t]*\\(alt\\)"
"alt keyword")
(defun mermaid-output-ext ()
"get the extendsion of generated file"
(if (eq mermaid-output-format 'svg)
".svg"
".png"))
;;;###autoload
(defun mermaid-compile ()
(interactive)
(let ((cmd (concat mermaid-compiler
(if (eq mermaid-output-format 'svg)
" --svg "
" ")
(if mermaid-verbose
" --verbose "
" ")
(buffer-file-name)))
(buf-name "*mermaid compilation")
(compilation-mode-hook (cons 'mermaid-compilation-mode-hook compilation-mode-hook)))
(if (fboundp 'compilation-start)
(compilation-start cmd nil
#'(lambda (mode-name)
buf-name))
(compile-internal cmd "No more errors" buf-name))))
;;;###autoload
(defun mermaid-view ()
(interactive)
(let ((dst-file-name (concat (buffer-file-name)
(mermaid-output-ext))))
(if (file-exists-p dst-file-name)
(find-file-other-window dst-file-name)
(error "Please compile the it first!\n"))))
;; disable debug in default
;;
(defvar mermaid-debug-enabled nil
"enable/disable debug")
(defmacro mermaid-debug (fmt &rest args)
`(when mermaid-debug-enabled
(message ,fmt ,@args)))
(defun mermaid-indent-line ()
"indent current line in mermaid mode"
(interactive)
(mermaid-debug "line no @ %d\n" (line-number-at-pos))
(beginning-of-line)
(if (bobp)
(indent-line-to 0)
(let (cur-indent)
(cond
((looking-at mermaid-end-scope-regexp)
(progn
(mermaid-debug "found end scope\n")
(save-excursion
(forward-line -1)
(if (or (looking-at mermaid-new-scope-regexp)
(looking-at mermaid-alt-regexp))
(setq cur-indent (current-indentation))
(setq cur-indent (- (current-indentation) default-tab-width)))
(if (< cur-indent 0)
(setq cur-indent 0)))))
((looking-at mermaid-section-regexp)
(let ((found-section nil)
(need-search t))
(mermaid-debug "found section\n")
(save-excursion
(while need-search
(forward-line -1)
(cond
((looking-at mermaid-section-regexp)
(progn
(mermaid-debug "found section\n")
(setq found-section t)
(setq cur-indent (current-indentation))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((or (looking-at mermaid-new-scope-regexp)
(looking-at mermaid-alt-regexp))
(progn
(mermaid-debug "found new scope\n")
(setq cur-indent (+ (current-indentation) default-tab-width))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((looking-at mermaid-end-scope-regexp)
(progn
(mermaid-debug "found end scope\n")
(setq cur-indent (current-indentation))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((bobp)
(progn
(setq cur-indent 0)
(setq need-search nil)))
(t t))))
(if (< cur-indent 0)
(setq cur-indent 0))))
((looking-at mermaid-else-regexp)
(let ((need-search t))
(mermaid-debug "else\n")
(save-excursion
(while need-search
(forward-line -1)
(cond
((or (looking-at mermaid-else-regexp)
(looking-at mermaid-alt-regexp))
(progn
(mermaid-debug "found matched alt/else\n")
(setq cur-indent (current-indentation))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((looking-at mermaid-end-scope-regexp)
(progn
(mermaid-debug "found end\n")
(setq cur-indent (- (current-indentation) default-tab-width))
(setq need-search nil)))
((bobp)
(progn
(setq cur-indent 0)))
(t t))))))
(t
(let ((need-search t)
(start-scope (looking-at mermaid-new-scope-regexp)))
(mermaid-debug "normal indent\n")
(save-excursion
(while need-search
(forward-line -1)
(cond
((looking-at mermaid-end-scope-regexp)
(progn
(mermaid-debug "found end scope\n")
(setq cur-indent (current-indentation))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((or (looking-at mermaid-new-scope-regexp)
(looking-at mermaid-alt-regexp))
(progn
(mermaid-debug "found begin scope\n")
(setq cur-indent (+ (current-indentation) default-tab-width))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((looking-at mermaid-section-regexp)
(progn
(mermaid-debug "found section \n")
(if start-scope
(setq cur-indent (current-indentation))
(setq cur-indent (+ (current-indentation) default-tab-width)))
(mermaid-debug "cur-indent %d\n" cur-indent)
(setq need-search nil)))
((bobp)
(progn
(setq cur-indent 0)
(setq need-search nil)))))))))
(if cur-indent
(indent-line-to cur-indent)
(indent-line-to 0)))))
;;;###autoload
(defun mermaid-mode ()
"Major mode for editing mermaid scripts"
(interactive)
(kill-all-local-variables)
(use-local-map mermaid-mode-map)
(set (make-local-variable 'font-lock-defaults)
'(mermaid-font-lock-keywords-1))
(set (make-local-variable 'indent-line-function)
'mermaid-indent-line)
(set (make-local-variable 'comment-start) "%%")
(set (make-local-variable 'comment-end) "\n")
(setq font-lock-keywords-case-fold-search t)
(setq major-mode 'mermaid-mode)
(setq mode-name "mermaid")
(run-hooks 'mermaid-mode-hook)
)
(provide 'mermaid-mode)