;;; puml-mode.el --- Major mode for PlantUML ;; Filename: puml-mode.el ;; Description: Major mode for PlantUML diagrams sources ;; Compatibility: Tested with Emacs 24.3 and 24.4 on OS X 10.10 ;; Author: Zhang Weize (zwz) ;; Maintainer: Carlo Sciolla (skuro) ;; Keywords: uml ascii ;; Version: 0.4 ;; 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 ;;; Commentary: ;; ;; 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 ;;; Change log: ;; ;; version 0.4, 2015-06-14 Use a puml- prefix to distinguish from the other plantuml-mode ;; version 0.3, 2015-06-13 Compatibility with Emacs 24.x ;; 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 ;;; Code: (require 'thingatpt) (defgroup puml-mode nil "Major mode for editing plantuml file." :group 'languages) (defcustom puml-plantuml-jar-path (expand-file-name "~/plantuml.jar") "The location of the PlantUML executable JAR.") (defvar puml-mode-hook nil "Standard hook for puml-mode.") (defvar puml-mode-version "0.4" "The puml-mode version string.") (defvar puml-mode-map nil "Keymap for puml-mode.") ;;; syntax table (defvar puml-mode-syntax-table (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) "Syntax table for `puml-mode'.") (defvar puml-plantuml-types nil) (defvar puml-plantuml-keywords nil) (defvar puml-plantuml-preprocessors nil) (defvar puml-plantuml-builtins nil) ;; keyword completion (defvar puml-plantuml-kwdList nil "The plantuml keywords.") ;;; font-lock (defun puml-init () "Initialize the keywords or builtins from the cmdline language output." (unless (file-exists-p puml-plantuml-jar-path) (error "Could not find plantuml.jar at %s" puml-plantuml-jar-path)) (with-temp-buffer (shell-command (concat "java -jar " (shell-quote-argument puml-plantuml-jar-path) " -language") (current-buffer)) (goto-char (point-min)) (let ((found (search-forward ";" nil t)) (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") (setq puml-plantuml-types (split-string (buffer-substring-no-properties pos (point))))) ((string= word "keyword") (setq puml-plantuml-keywords (split-string (buffer-substring-no-properties pos (point))))) ((string= word "preprocessor") (setq puml-plantuml-preprocessors (split-string (buffer-substring-no-properties pos (point))))) (t (setq puml-plantuml-builtins (append puml-plantuml-builtins (split-string (buffer-substring-no-properties pos (point))))))) ;; ((string= word "skinparameter") ;; ((string= word "color"))) (setq found (search-forward ";" nil nil))))))) (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))) (setq puml-font-lock-keywords `( (,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) ;; note: order matters )) (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) ;; clear memory (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 () "Perform keyword completion on word before cursor." (interactive) (let ((posEnd (point)) (meat (thing-at-point 'symbol)) maxMatchResult) (when (not meat) (setq meat "")) (setq maxMatchResult (try-completion meat puml-plantuml-kwdList)) (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 (all-completions meat puml-plantuml-kwdList) meat)) (message "Making completion list...%s" "done"))))) (add-to-list 'auto-mode-alist '("\\.pum$" . puml-mode)) ;;;###autoload (defun puml-mode () "Major mode for plantuml. Shortcuts Command Name \\[puml-complete-symbol] `puml-complete-symbol'" (interactive) (kill-all-local-variables) ;; (python-mode) ; for indentation (setq major-mode 'puml-mode mode-name "puml") (set-syntax-table puml-mode-syntax-table) (use-local-map puml-mode-map) (make-local-variable 'font-lock-defaults) (setq font-lock-defaults '((puml-font-lock-keywords) nil t)) (run-mode-hooks 'puml-mode-hook)) (provide 'puml-mode) ;;; puml-mode.el ends here