2016-10-17 14:24:31 +00:00
;;; plantuml-mode.el --- Major mode for PlantUML -*- lexical-binding: t; -*-
;; Filename: plantuml-mode.el
;; Description: Major mode for PlantUML diagrams sources
;; Compatibility: Tested with Emacs 24.3 through 24.5 on OS X 10.10
;; Author: Zhang Weize (zwz)
;; Maintainer: Carlo Sciolla (skuro)
;; Keywords: uml plantuml ascii
2016-10-18 10:52:16 +00:00
;; Package-Version: 20161018.1025
;; Version: 1.1.0
2016-10-17 14:24:31 +00:00
;; Package-Requires: ((emacs "24"))
;; 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:
;;
2016-10-18 10:52:16 +00:00
;; version 1.1.0, 2016-10-18 Make PlantUML run headless by default; introduced custom variable `plantuml-java-args' to control which arguments are passed to Plantuml.
;; version 1.0.1, 2016-10-17 Bugfix release: proper auto-mode-alist regex; init delayed at mode load; avoid calling hooks twice.
;; version 1.0.0, 2016-10-16 Moved the mode to plantuml-mode, superseding zwz/plantuml-mode and skuro/puml-mode. Added preview for the currently selected region.
2016-10-17 14:24:31 +00:00
;; version 0.6.7, 2016-10-11 [from puml-mode] Added deprecation warning in favor of plantuml-mode
;; version 0.6.6, 2016-07-19 [from puml-mode] Added autoload, minor bug fixes
;; version 0.6.5, 2016-03-24 [from puml-mode] Added UTF8 support and open in new window / frame shortcuts
;; version 0.6.4, 2015-12-12 [from puml-mode] Added support for comments (single and multiline) -- thanks to https://github.com/nivekuil
;; version 0.6.3, 2015-11-07 [from puml-mode] Added per-buffer configurability of output type (thanks to https://github.com/davazp)
;; version 0.6.2, 2015-11-07 [from puml-mode] Added debugging capabilities to improve issue analysis
;; version 0.6.1, 2015-09-26 [from puml-mode] Bugfix: use eq to compare symbols instead of cl-equalp
;; version 0.6, 2015-09-26 [from puml-mode] Fixed PNG preview
;; version 0.5, 2015-09-21 [from puml-mode] Added preview capabilities
;; version 0.4, 2015-06-14 [from puml-mode] Use a puml- prefix to distinguish from the other plantuml-mode
;; version 0.3, 2015-06-13 [from puml-mode] Compatibility with Emacs 24.x
;; version 0.2, 2010-09-20 [from puml-mode] Initialize the keywords from the -language output of plantuml.jar instead of the hard-coded way.
;; version 0.1, 2010-08-25 [from puml-mode] First version
;;; Code:
( require 'thingatpt )
( defgroup plantuml-mode nil
" Major mode for editing plantuml file. "
:group 'languages )
( defcustom plantuml-jar-path
( expand-file-name " ~/plantuml.jar " )
" The location of the PlantUML executable JAR. " )
( defvar plantuml-mode-hook nil " Standard hook for plantuml-mode. " )
( defconst plantuml-mode-version " 0.6.8 " " The plantuml-mode version string. " )
( defvar plantuml-mode-debug-enabled nil )
( defvar plantuml-font-lock-keywords nil )
( defvar plantuml-mode-map
( let ( ( keymap ( make-sparse-keymap ) ) )
( define-key keymap ( kbd " C-c C-c " ) 'plantuml-preview )
keymap )
" Keymap for plantuml-mode. " )
2016-10-18 10:52:16 +00:00
( defcustom plantuml-java-command " java "
" The java command used to execute PlantUML. " )
2016-10-17 14:24:31 +00:00
2016-10-18 10:52:16 +00:00
( defcustom plantuml-java-args ' ( " -Djava.awt.headless=true " " -jar " )
" The parameters passed to `plantuml-java-command' when executing PlantUML. " )
2016-10-17 14:24:31 +00:00
2016-10-18 10:52:16 +00:00
( defcustom plantuml-suppress-deprecation-warning t
" To silence the deprecation warning when `puml-mode' is found upon loading. " )
( defun plantuml-command-line ( )
" Compose the PlantUML command line as a string. "
( mapconcat 'identity ( cons plantuml-java-command plantuml-java-args ) " " ) )
2016-10-17 14:24:31 +00:00
( defun plantuml-render-command ( &rest arguments )
" Create a command line to execute PlantUML with arguments (as ARGUMENTS). "
2016-10-18 10:52:16 +00:00
( let ( ( cmd ( concat ( plantuml-command-line ) " " ( shell-quote-argument plantuml-jar-path ) ) )
( argstring ( mapconcat 'identity arguments " " ) ) )
( plantuml-debug ( format " Command is %s " cmd ) )
2016-10-17 14:24:31 +00:00
( concat cmd " " argstring ) ) )
;;; syntax table
( defvar plantuml-mode-syntax-table
( let ( ( synTable ( make-syntax-table ) ) )
2016-10-18 10:52:16 +00:00
( modify-syntax-entry ?\/ " . 14c " synTable )
( modify-syntax-entry ?' " < 23 " synTable )
2016-10-17 14:24:31 +00:00
( modify-syntax-entry ?\n " > " synTable )
( modify-syntax-entry ?\r " > " synTable )
( modify-syntax-entry ?! " w " synTable )
( modify-syntax-entry ?@ " w " synTable )
( modify-syntax-entry ?# " ' " synTable )
synTable )
" Syntax table for `plantuml-mode' . " )
( defvar plantuml-types nil )
( defvar plantuml-keywords nil )
( defvar plantuml-preprocessors nil )
( defvar plantuml-builtins nil )
;; keyword completion
( defvar plantuml-kwdList nil " The plantuml keywords. " )
( defun plantuml-enable-debug ( )
" Enables debug messages into the *PLANTUML Messages* buffer. "
( interactive )
( setq plantuml-mode-debug-enabled t ) )
( defun plantuml-disable-debug ( )
" Stops any debug messages to be added into the *PLANTUML Messages* buffer. "
( interactive )
( setq plantuml-mode-debug-enabled nil ) )
( defun plantuml-debug ( msg )
" Writes msg (as MSG) into the *PLANTUML Messages* buffer without annoying the user. "
( if plantuml-mode-debug-enabled
( let* ( ( log-buffer-name " *PLANTUML Messages* " )
( log-buffer ( get-buffer-create log-buffer-name ) ) )
( save-excursion
( with-current-buffer log-buffer
( goto-char ( point-max ) )
( insert msg )
( insert " \n " ) ) ) ) ) )
( defun plantuml-init ( )
" Initialize the keywords or builtins from the cmdline language output. "
( unless ( file-exists-p plantuml-jar-path )
( error " Could not find plantuml.jar at %s " plantuml-jar-path ) )
( with-temp-buffer
2016-10-18 10:52:16 +00:00
( let ( ( cmd ( plantuml-render-command " -charset UTF-8 -language " ) ) )
( shell-command cmd ( current-buffer ) )
( goto-char ( point-min ) ) )
2016-10-17 14:24:31 +00:00
( 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 plantuml-types
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
( ( string= word " keyword " )
( setq plantuml-keywords
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
( ( string= word " preprocessor " )
( setq plantuml-preprocessors
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
( t ( setq plantuml-builtins
( append
plantuml-builtins
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) ) ) )
( setq found ( search-forward " ; " nil nil ) ) ) ) ) ) )
( defconst plantuml-preview-buffer " *PLANTUML Preview* " )
( defvar plantuml-output-type
( if ( not ( display-images-p ) )
" utxt "
( cond ( ( image-type-available-p 'svg ) " svg " )
( ( image-type-available-p 'png ) " png " )
( t " utxt " ) ) )
" Specify the desired output type to use for generated diagrams. " )
( defun plantuml-read-output-type ( )
" Read from the minibuffer a output type. "
( let* ( ( completion-ignore-case t )
( available-types
( append
( and ( image-type-available-p 'svg ) ' ( " svg " ) )
( and ( image-type-available-p 'png ) ' ( " png " ) )
' ( " utxt " ) ) ) )
( completing-read ( format " Output type [%s]: " plantuml-output-type )
available-types
nil
t
nil
nil
plantuml-output-type ) ) )
( defun plantuml-set-output-type ( type )
" Set the desired output type (as TYPE) for the current buffer.
If the
major mode of the current buffer mode is not plantuml-mode, set the
default output type for new buffers. "
( interactive ( list ( plantuml-read-output-type ) ) )
( setq plantuml-output-type type ) )
( defun plantuml-is-image-output-p ( )
" Return true if the diagram output format is an image, false if it's text based. "
( not ( equal " utxt " plantuml-output-type ) ) )
( defun plantuml-output-type-opt ( )
" Create the flag to pass to PlantUML to produce the selected output format. "
( concat " -t " plantuml-output-type ) )
2016-10-18 10:52:16 +00:00
( defmacro plantuml-start-process ( buf )
" Run PlantUML as an Emacs process and puts the output into the given buffer (as BUF). "
` ( start-process " PLANTUML " , buf
plantuml-java-command
,@ plantuml-java-args
( shell-quote-argument plantuml-jar-path )
( plantuml-output-type-opt ) " -p " ) )
2016-10-17 14:24:31 +00:00
( defun plantuml-preview-string ( prefix string )
" Preview diagram from PlantUML sources (as STRING), using prefix (as PREFIX)
to choose where to display it:
- 4 ( when prefixing the command with C-u ) -> new window
- 16 ( when prefixing the command with C-u C-u ) -> new frame.
- else -> new buffer "
( let ( ( b ( get-buffer plantuml-preview-buffer ) ) )
( when b
( kill-buffer b ) ) )
( let* ( ( imagep ( and ( display-images-p )
( plantuml-is-image-output-p ) ) )
( process-connection-type nil )
( buf ( get-buffer-create plantuml-preview-buffer ) )
( coding-system-for-read ( and imagep 'binary ) )
( coding-system-for-write ( and imagep 'binary ) ) )
2016-10-18 10:52:16 +00:00
( let ( ( ps ( plantuml-start-process buf ) ) )
2016-10-17 14:24:31 +00:00
( process-send-string ps string )
( process-send-eof ps )
( set-process-sentinel ps
( lambda ( _ps event )
( unless ( equal event " finished \n " )
( error " PLANTUML Preview failed: %s " event ) )
( cond
( ( = prefix 16 )
( switch-to-buffer-other-frame plantuml-preview-buffer ) )
( ( = prefix 4 )
( switch-to-buffer-other-window plantuml-preview-buffer ) )
( t ( switch-to-buffer plantuml-preview-buffer ) ) )
( when imagep
( image-mode )
( set-buffer-multibyte t ) ) ) ) ) ) )
( defun plantuml-preview-buffer ( prefix )
" Preview diagram from the PlantUML sources in the current buffer.
Uses prefix ( as PREFIX ) to choose where to display it:
- 4 ( when prefixing the command with C-u ) -> new window
- 16 ( when prefixing the command with C-u C-u ) -> new frame.
- else -> new buffer "
( interactive " p " )
( plantuml-preview-string prefix ( buffer-string ) ) )
( defun plantuml-preview-region ( prefix )
" Preview diagram from the PlantUML sources in the current region.
Uses prefix ( as PREFIX ) to choose where to display it:
- 4 ( when prefixing the command with C-u ) -> new window
- 16 ( when prefixing the command with C-u C-u ) -> new frame.
- else -> new buffer "
( interactive " p " )
( plantuml-preview-string prefix ( concat " @startuml \n "
( buffer-substring-no-properties
( region-beginning ) ( region-end ) )
" \n @enduml " ) ) )
( defun plantuml-preview ( prefix )
" Preview diagram from the PlantUML sources.
Uses the current region if one is active, or the entire buffer otherwise.
Uses prefix ( as PREFIX ) to choose where to display it:
- 4 ( when prefixing the command with C-u ) -> new window
- 16 ( when prefixing the command with C-u C-u ) -> new frame.
- else -> new buffer "
( interactive " p " )
( if mark-active
( plantuml-preview-region prefix )
( plantuml-preview-buffer prefix ) ) )
2016-10-18 10:52:16 +00:00
( defun plantuml-init-once ( )
" Ensure initialization only happens once. "
( unless plantuml-kwdList
( plantuml-init )
( defvar plantuml-types-regexp ( concat " ^ \\ s * \\ ( " ( regexp-opt plantuml-types 'words ) " \\ | \\ < \\ (note \\ s +over \\ |note \\ s + \\ (left \\ |right \\ |bottom \\ |top \\ ) \\ s + \\ (of \\ )? \\ ) \\ > \\ | \\ < \\ ( \\ (left \\ |center \\ |right \\ ) \\ s + \\ (header \\ |footer \\ ) \\ ) \\ > \\ ) " ) )
( defvar plantuml-keywords-regexp ( concat " ^ \\ s * " ( regexp-opt plantuml-keywords 'words ) " \\ | \\ (< \\ |<| \\ | \\ * \\ |o \\ ) \\ ( \\ .+ \\ |-+ \\ ) \\ | \\ ( \\ .+ \\ |-+ \\ ) \\ (> \\ ||> \\ | \\ * \\ |o \\ ) \\ | \\ . \\ {2, \\ } \\ |- \\ {2, \\ } " ) )
( defvar plantuml-builtins-regexp ( regexp-opt plantuml-builtins 'words ) )
( defvar plantuml-preprocessors-regexp ( concat " ^ \\ s * " ( regexp-opt plantuml-preprocessors 'words ) ) )
( setq plantuml-font-lock-keywords
` (
( , plantuml-types-regexp . font-lock-type-face )
( , plantuml-keywords-regexp . font-lock-keyword-face )
( , plantuml-builtins-regexp . font-lock-builtin-face )
( , plantuml-preprocessors-regexp . font-lock-preprocessor-face )
;; note: order matters
) )
( setq plantuml-kwdList ( make-hash-table :test 'equal ) )
( mapc ( lambda ( x ) ( puthash x t plantuml-kwdList ) ) plantuml-types )
( mapc ( lambda ( x ) ( puthash x t plantuml-kwdList ) ) plantuml-keywords )
( mapc ( lambda ( x ) ( puthash x t plantuml-kwdList ) ) plantuml-builtins )
( mapc ( lambda ( x ) ( puthash x t plantuml-kwdList ) ) plantuml-preprocessors )
( put 'plantuml-kwdList 'risky-local-variable t )
;; clear memory
( setq plantuml-types nil )
( setq plantuml-keywords nil )
( setq plantuml-builtins nil )
( setq plantuml-preprocessors nil )
( setq plantuml-types-regexp nil )
( setq plantuml-keywords-regexp nil )
( setq plantuml-builtins-regexp nil )
( setq plantuml-preprocessors-regexp nil ) ) )
2016-10-17 14:24:31 +00:00
( defun plantuml-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 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 plantuml-kwdList ) ) )
( message " Making completion list...%s " " done " ) ) ) ) )
;;;###autoload
2016-10-18 10:52:16 +00:00
( add-to-list 'auto-mode-alist ' ( " \\ .(plantuml \\ |pum \\ |plantuml \\ |plu) \\ ' " . plantuml-mode ) )
2016-10-17 14:24:31 +00:00
;;;###autoload
( define-derived-mode plantuml-mode prog-mode " plantuml "
" Major mode for plantuml.
Shortcuts Command Name
\\ [ plantuml-complete-symbol ] ` plantuml-complete-symbol ' "
2016-10-18 10:52:16 +00:00
( plantuml-init-once )
2016-10-17 14:24:31 +00:00
( make-local-variable 'plantuml-output-type )
( set ( make-local-variable 'comment-start-skip ) " \\ ('+ \\ |/'+ \\ ) \\ s * " )
( set ( make-local-variable 'comment-start ) " /' " )
( set ( make-local-variable 'comment-end ) " '/ " )
( set ( make-local-variable 'comment-multi-line ) t )
( set ( make-local-variable 'comment-style ) 'extra-line )
2016-10-18 10:52:16 +00:00
( setq font-lock-defaults ' ( ( plantuml-font-lock-keywords ) nil t ) ) )
2016-10-17 14:24:31 +00:00
( defun plantuml-deprecation-warning ( )
" Warns the user about the deprecation of the `puml-mode' project. "
( if ( and plantuml-suppress-deprecation-warning
( featurep 'puml-mode ) )
( display-warning :warning
" `puml-mode' is now deprecated and no longer updated, but it's still present in your system. \
You should move your configuration to use ` plantuml-mode '. See https://github.com/sytac/plantuml-mode. \
See more at https://github.com/skuro/puml-mode/issues/26 " )))
( add-hook 'plantuml-mode-hook 'plantuml-deprecation-warning )
( provide 'plantuml-mode )
;;; plantuml-mode.el ends here