2015-07-13 23:04:33 +02:00
|
|
|
;;; puml-mode.el --- Major mode for PlantUML
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-14 16:50:41 +02:00
|
|
|
;; Filename: puml-mode.el
|
|
|
|
;; Description: Major mode for PlantUML diagrams sources
|
2015-09-21 17:28:46 +02:00
|
|
|
;; Compatibility: Tested with Emacs 24.3 through 24.5 on OS X 10.10
|
2012-06-11 13:31:49 +02:00
|
|
|
;; Author: Zhang Weize (zwz)
|
2015-07-13 23:04:33 +02:00
|
|
|
;; Maintainer: Carlo Sciolla (skuro)
|
2015-09-21 17:28:46 +02:00
|
|
|
;; Keywords: uml plantuml ascii
|
2015-12-07 16:50:56 +01:00
|
|
|
;; Version: 0.6.2
|
2012-06-11 13:31:49 +02:00
|
|
|
|
|
|
|
;; You can redistribute this program and/or modify it under the terms
|
|
|
|
;; of the GNU General Public License as published by the Free Software
|
|
|
|
;; Foundation; either version 2
|
|
|
|
;; NOTE: licensing fixed to GPLv2 as per original author comment
|
|
|
|
|
2015-07-14 16:50:41 +02:00
|
|
|
;;; Commentary:
|
|
|
|
;;
|
2012-06-11 13:31:49 +02:00
|
|
|
;; A major mode for plantuml, see: http://plantuml.sourceforge.net/
|
|
|
|
;; Plantuml is an open-source tool in java that allows to quickly write :
|
|
|
|
;; - sequence diagram,
|
|
|
|
;; - use case diagram,
|
|
|
|
;; - class diagram,
|
|
|
|
;; - activity diagram,
|
|
|
|
;; - component diagram,
|
|
|
|
;; - state diagram
|
|
|
|
;; - object diagram
|
2015-07-14 16:50:41 +02:00
|
|
|
|
|
|
|
;;; Change log:
|
|
|
|
;;
|
2015-12-07 16:50:56 +01:00
|
|
|
;; version 0.6.2, 2015-11-07 Added debugging capabilities to improve issue analysis
|
2015-09-26 11:22:45 +02:00
|
|
|
;; version 0.6.1, 2015-09-26 Bugfix: use eq to compare symbols instead of cl-equalp
|
2015-09-26 09:29:52 +02:00
|
|
|
;; version 0.6, 2015-09-26 Fixed PNG preview
|
2015-09-21 17:28:46 +02:00
|
|
|
;; version 0.5, 2015-09-21 Added preview capabilities
|
2015-07-14 16:50:41 +02:00
|
|
|
;; version 0.4, 2015-06-14 Use a puml- prefix to distinguish from the other plantuml-mode
|
2015-07-13 23:04:33 +02:00
|
|
|
;; version 0.3, 2015-06-13 Compatibility with Emacs 24.x
|
2012-06-11 13:31:49 +02:00
|
|
|
;; version 0.2, 2010-09-20 Initialize the keywords from the -language output of plantuml.jar instead of the hard-coded way.
|
|
|
|
;; version 0.1, 2010-08-25 First version
|
|
|
|
|
2015-07-13 23:04:33 +02:00
|
|
|
;;; Code:
|
2012-06-11 13:31:49 +02:00
|
|
|
(require 'thingatpt)
|
|
|
|
|
2015-07-13 23:04:33 +02:00
|
|
|
(defgroup puml-mode nil
|
2012-06-11 13:31:49 +02:00
|
|
|
"Major mode for editing plantuml file."
|
|
|
|
:group 'languages)
|
|
|
|
|
2015-07-14 17:02:09 +02:00
|
|
|
(defcustom puml-plantuml-jar-path
|
|
|
|
(expand-file-name "~/plantuml.jar")
|
|
|
|
"The location of the PlantUML executable JAR.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(defvar puml-mode-hook nil "Standard hook for puml-mode.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-09-26 12:40:17 +02:00
|
|
|
(defvar puml-mode-version "0.6.1" "The puml-mode version string.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-12-07 16:45:40 +01:00
|
|
|
(defvar puml-mode-debug-enabled nil)
|
|
|
|
|
2015-09-26 11:41:33 +02:00
|
|
|
(defvar puml-mode-map
|
|
|
|
(let ((keymap (make-keymap)))
|
|
|
|
(define-key keymap (kbd "C-c C-c") 'puml-preview)
|
|
|
|
keymap)
|
|
|
|
"Keymap for puml-mode.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
|
|
|
;;; syntax table
|
2015-07-14 16:40:02 +02:00
|
|
|
(defvar puml-mode-syntax-table
|
2012-06-11 13:31:49 +02:00
|
|
|
(let ((synTable (make-syntax-table)))
|
|
|
|
(modify-syntax-entry ?' "< b" synTable)
|
|
|
|
(modify-syntax-entry ?\n "> b" synTable)
|
|
|
|
(modify-syntax-entry ?! "w" synTable)
|
|
|
|
(modify-syntax-entry ?@ "w" synTable)
|
|
|
|
(modify-syntax-entry ?# "'" synTable)
|
|
|
|
synTable)
|
2015-07-13 23:06:41 +02:00
|
|
|
"Syntax table for `puml-mode'.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(defvar puml-plantuml-types nil)
|
|
|
|
(defvar puml-plantuml-keywords nil)
|
|
|
|
(defvar puml-plantuml-preprocessors nil)
|
|
|
|
(defvar puml-plantuml-builtins nil)
|
2012-06-11 13:31:49 +02:00
|
|
|
|
|
|
|
;; keyword completion
|
2015-07-14 16:50:41 +02:00
|
|
|
(defvar puml-plantuml-kwdList nil "The plantuml keywords.")
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-12-07 16:45:40 +01:00
|
|
|
(defun puml-enable-debug ()
|
|
|
|
"Enables debug messages into the *PUML Messages* buffer."
|
2015-12-07 16:49:21 +01:00
|
|
|
(interactive)
|
2015-12-07 16:45:40 +01:00
|
|
|
(setq puml-mode-debug-enabled t))
|
|
|
|
|
|
|
|
(defun puml-disable-debug ()
|
|
|
|
"Stops any debug messages to be added into the *PUML Messages* buffer."
|
2015-12-07 16:49:21 +01:00
|
|
|
(interactive)
|
2015-12-07 16:45:40 +01:00
|
|
|
(setq puml-mode-debug-enabled nil))
|
|
|
|
|
|
|
|
(defun puml-debug (msg)
|
|
|
|
"Writes msg (as MSG) into the *PUML Messages* buffer without annoying the user."
|
|
|
|
(if puml-mode-debug-enabled
|
|
|
|
(let* ((log-buffer-name "*PUML Messages*")
|
|
|
|
(log-buffer (get-buffer-create log-buffer-name)))
|
|
|
|
(save-excursion
|
|
|
|
(with-current-buffer log-buffer
|
|
|
|
(goto-char (point-max))
|
|
|
|
(insert msg)
|
|
|
|
(insert "\n"))))))
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(defun puml-init ()
|
2015-07-14 16:50:41 +02:00
|
|
|
"Initialize the keywords or builtins from the cmdline language output."
|
2015-07-14 16:40:02 +02:00
|
|
|
(unless (file-exists-p puml-plantuml-jar-path)
|
|
|
|
(error "Could not find plantuml.jar at %s" puml-plantuml-jar-path))
|
2012-06-11 13:31:49 +02:00
|
|
|
(with-temp-buffer
|
|
|
|
(shell-command (concat "java -jar "
|
2015-07-14 16:40:02 +02:00
|
|
|
(shell-quote-argument puml-plantuml-jar-path)
|
2012-06-11 13:31:49 +02:00
|
|
|
" -language") (current-buffer))
|
|
|
|
(goto-char (point-min))
|
2015-07-02 19:09:55 +02:00
|
|
|
(let ((found (search-forward ";" nil t))
|
2012-06-11 13:31:49 +02:00
|
|
|
(word "")
|
|
|
|
(count 0)
|
|
|
|
(pos 0))
|
|
|
|
(while found
|
|
|
|
(forward-char)
|
|
|
|
(setq word (current-word))
|
|
|
|
(if (string= word "EOF") (setq found nil)
|
|
|
|
;; else
|
|
|
|
(forward-line)
|
|
|
|
(setq count (string-to-number (current-word)))
|
|
|
|
(beginning-of-line 2)
|
|
|
|
(setq pos (point))
|
|
|
|
(forward-line count)
|
|
|
|
(cond ((string= word "type")
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-plantuml-types
|
2012-06-11 13:31:49 +02:00
|
|
|
(split-string
|
|
|
|
(buffer-substring-no-properties pos (point)))))
|
|
|
|
((string= word "keyword")
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-plantuml-keywords
|
2012-06-11 13:31:49 +02:00
|
|
|
(split-string
|
|
|
|
(buffer-substring-no-properties pos (point)))))
|
|
|
|
((string= word "preprocessor")
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-plantuml-preprocessors
|
2012-06-11 13:31:49 +02:00
|
|
|
(split-string
|
|
|
|
(buffer-substring-no-properties pos (point)))))
|
2015-07-14 16:40:02 +02:00
|
|
|
(t (setq puml-plantuml-builtins
|
2012-06-11 13:31:49 +02:00
|
|
|
(append
|
2015-07-14 16:40:02 +02:00
|
|
|
puml-plantuml-builtins
|
2012-06-11 13:31:49 +02:00
|
|
|
(split-string
|
|
|
|
(buffer-substring-no-properties pos (point)))))))
|
|
|
|
(setq found (search-forward ";" nil nil)))))))
|
|
|
|
|
2015-09-14 12:04:32 +02:00
|
|
|
(defconst puml-preview-buffer "*PUML Preview*")
|
|
|
|
|
2015-09-21 17:03:33 +02:00
|
|
|
(defun puml-output-type ()
|
|
|
|
"Detects the best output type to use for generated diagrams."
|
|
|
|
(cond ((image-type-available-p 'svg) 'svg)
|
2015-09-26 09:24:22 +02:00
|
|
|
((image-type-available-p 'png) 'png)
|
2015-09-21 17:03:33 +02:00
|
|
|
('utxt)))
|
|
|
|
|
|
|
|
(defun puml-is-image-output-p ()
|
|
|
|
"Return true if the diagram output format is an image, false if it's text based."
|
2015-09-26 11:13:53 +02:00
|
|
|
(not (eq 'utxt (puml-output-type))))
|
2015-09-21 17:03:33 +02:00
|
|
|
|
2015-09-14 12:04:32 +02:00
|
|
|
(defun puml-output-type-opt ()
|
2015-09-21 17:03:33 +02:00
|
|
|
"Create the flag to pass to PlantUML to produce the selected output format."
|
|
|
|
(let ((type (puml-output-type)))
|
|
|
|
(concat "-t" (symbol-name type))))
|
|
|
|
|
|
|
|
(defun puml-preview-sentinel (ps event)
|
|
|
|
"For the PlantUML process (as PS) reacts on the termination event (as EVENT)."
|
|
|
|
(if (equal event "finished\n")
|
2015-09-14 12:04:32 +02:00
|
|
|
(progn
|
|
|
|
(switch-to-buffer puml-preview-buffer)
|
2015-09-21 17:03:33 +02:00
|
|
|
(when (and (display-images-p)
|
|
|
|
(puml-is-image-output-p))
|
|
|
|
(image-mode)))
|
|
|
|
(warn "PUML Preview failed: %s" event)))
|
2015-09-14 12:04:32 +02:00
|
|
|
|
|
|
|
(defun puml-preview ()
|
2015-09-21 17:03:33 +02:00
|
|
|
"Preview diagram."
|
2015-09-14 12:04:32 +02:00
|
|
|
(interactive)
|
|
|
|
(let ((b (get-buffer puml-preview-buffer)))
|
|
|
|
(when b
|
|
|
|
(kill-buffer b)))
|
|
|
|
(let ((process-connection-type nil)
|
2015-09-26 09:24:22 +02:00
|
|
|
(buf (get-buffer-create puml-preview-buffer))
|
|
|
|
(coding-system-for-read 'binary)
|
2015-12-07 16:45:40 +01:00
|
|
|
(coding-system-for-write 'binary)
|
|
|
|
(command-params (shell-quote-argument puml-plantuml-jar-path)))
|
|
|
|
(puml-debug "Executing:")
|
|
|
|
(puml-debug (concat "java -jar " command-params " " (puml-output-type-opt) " -p"))
|
2015-09-14 12:04:32 +02:00
|
|
|
(let ((ps (start-process "PUML" buf
|
2015-12-07 16:45:40 +01:00
|
|
|
"java" "-jar" command-params
|
2015-09-14 12:04:32 +02:00
|
|
|
(puml-output-type-opt) "-p")))
|
|
|
|
(process-send-region ps (point-min) (point-max))
|
|
|
|
(process-send-eof ps)
|
|
|
|
(set-process-sentinel ps 'puml-preview-sentinel))))
|
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(unless puml-plantuml-kwdList
|
|
|
|
(puml-init)
|
|
|
|
(defvar puml-plantuml-types-regexp (concat "^\\s *\\(" (regexp-opt puml-plantuml-types 'words) "\\|\\<\\(note\\s +over\\|note\\s +\\(left\\|right\\|bottom\\|top\\)\\s +\\(of\\)?\\)\\>\\|\\<\\(\\(left\\|center\\|right\\)\\s +\\(header\\|footer\\)\\)\\>\\)"))
|
|
|
|
(defvar puml-plantuml-keywords-regexp (concat "^\\s *" (regexp-opt puml-plantuml-keywords 'words) "\\|\\(<\\|<|\\|\\*\\|o\\)\\(\\.+\\|-+\\)\\|\\(\\.+\\|-+\\)\\(>\\||>\\|\\*\\|o\\)\\|\\.\\{2,\\}\\|-\\{2,\\}"))
|
|
|
|
(defvar puml-plantuml-builtins-regexp (regexp-opt puml-plantuml-builtins 'words))
|
|
|
|
(defvar puml-plantuml-preprocessors-regexp (concat "^\\s *" (regexp-opt puml-plantuml-preprocessors 'words)))
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-font-lock-keywords
|
2012-06-11 13:31:49 +02:00
|
|
|
`(
|
2015-07-14 16:40:02 +02:00
|
|
|
(,puml-plantuml-types-regexp . font-lock-type-face)
|
|
|
|
(,puml-plantuml-keywords-regexp . font-lock-keyword-face)
|
|
|
|
(,puml-plantuml-builtins-regexp . font-lock-builtin-face)
|
|
|
|
(,puml-plantuml-preprocessors-regexp . font-lock-preprocessor-face)
|
2012-06-11 13:31:49 +02:00
|
|
|
;; note: order matters
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-plantuml-kwdList (make-hash-table :test 'equal))
|
|
|
|
(mapc (lambda (x) (puthash x t puml-plantuml-kwdList)) puml-plantuml-types)
|
|
|
|
(mapc (lambda (x) (puthash x t puml-plantuml-kwdList)) puml-plantuml-keywords)
|
|
|
|
(mapc (lambda (x) (puthash x t puml-plantuml-kwdList)) puml-plantuml-builtins)
|
|
|
|
(mapc (lambda (x) (puthash x t puml-plantuml-kwdList)) puml-plantuml-preprocessors)
|
|
|
|
(put 'puml-plantuml-kwdList 'risky-local-variable t)
|
2012-06-11 13:31:49 +02:00
|
|
|
|
|
|
|
;; clear memory
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq puml-plantuml-types nil)
|
|
|
|
(setq puml-plantuml-keywords nil)
|
|
|
|
(setq puml-plantuml-builtins nil)
|
|
|
|
(setq puml-plantuml-preprocessors nil)
|
|
|
|
(setq puml-plantuml-types-regexp nil)
|
|
|
|
(setq puml-plantuml-keywords-regexp nil)
|
|
|
|
(setq puml-plantuml-builtins-regexp nil)
|
|
|
|
(setq puml-plantuml-preprocessors-regexp nil))
|
|
|
|
|
|
|
|
(defun puml-complete-symbol ()
|
2012-06-11 13:31:49 +02:00
|
|
|
"Perform keyword completion on word before cursor."
|
|
|
|
(interactive)
|
|
|
|
(let ((posEnd (point))
|
|
|
|
(meat (thing-at-point 'symbol))
|
|
|
|
maxMatchResult)
|
|
|
|
|
|
|
|
(when (not meat) (setq meat ""))
|
|
|
|
|
2015-07-14 16:40:02 +02:00
|
|
|
(setq maxMatchResult (try-completion meat puml-plantuml-kwdList))
|
2012-06-11 13:31:49 +02:00
|
|
|
(cond ((eq maxMatchResult t))
|
|
|
|
((null maxMatchResult)
|
|
|
|
(message "Can't find completion for \"%s\"" meat)
|
|
|
|
(ding))
|
|
|
|
((not (string= meat maxMatchResult))
|
|
|
|
(delete-region (- posEnd (length meat)) posEnd)
|
|
|
|
(insert maxMatchResult))
|
|
|
|
(t (message "Making completion list...")
|
|
|
|
(with-output-to-temp-buffer "*Completions*"
|
|
|
|
(display-completion-list
|
2015-09-21 19:32:30 +02:00
|
|
|
(all-completions meat puml-plantuml-kwdList)))
|
2012-06-11 13:31:49 +02:00
|
|
|
(message "Making completion list...%s" "done")))))
|
|
|
|
|
2015-07-13 23:06:41 +02:00
|
|
|
(add-to-list 'auto-mode-alist '("\\.pum$" . puml-mode))
|
2012-06-11 13:31:49 +02:00
|
|
|
|
|
|
|
;;;###autoload
|
2015-09-26 11:41:33 +02:00
|
|
|
(define-derived-mode puml-mode prog-mode "puml"
|
2012-06-11 13:31:49 +02:00
|
|
|
"Major mode for plantuml.
|
|
|
|
|
|
|
|
Shortcuts Command Name
|
2015-07-14 16:40:02 +02:00
|
|
|
\\[puml-complete-symbol] `puml-complete-symbol'"
|
2015-09-26 11:41:33 +02:00
|
|
|
(setq font-lock-defaults '((puml-font-lock-keywords) nil t)))
|
2012-06-11 13:31:49 +02:00
|
|
|
|
2015-07-13 23:04:33 +02:00
|
|
|
(provide 'puml-mode)
|
|
|
|
;;; puml-mode.el ends here
|