2016-10-10 20:30:00 +02:00
;;; plantuml-mode.el --- Major mode for PlantUML -*- lexical-binding: t; -*-
2012-06-11 13:31:49 +02:00
2016-10-10 20:30:00 +02:00
;; Filename: plantuml-mode.el
2015-07-14 16:50:41 +02:00
;; Description: Major mode for PlantUML diagrams sources
2018-08-14 21:59:34 +02:00
;; Compatibility: Tested with Emacs 25 through 27 (current master)
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
2019-03-16 10:53:17 +01:00
;; Version: 1.2.9
2019-03-16 12:58:06 +01:00
;; Package-Version: 1.2.9
2019-05-10 08:57:46 +02:00
;; Package-Requires: ((dash "2.0.0") (emacs "25.0"))
2012-06-11 13:31:49 +02:00
2017-08-18 12:16:12 +02:00
;; This file 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, or (at your option)
;; any later version.
;; This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
2012-06-11 13:31:49 +02:00
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:
;;
2019-08-21 16:42:54 +02:00
;; version 1.4.0, 2019-08-21 Added `executable' exec mode to use locally installed `plantuml' binaries, various bugfixes
2019-08-03 15:56:41 +02:00
;; version 1.3.1, 2019-08-02 Fixed interactive behavior of `plantuml-set-exec-mode'
2019-05-31 10:53:40 +02:00
;; version 1.3.0, 2019-05-31 Added experimental support for multiple rendering modes and, specifically, preview using a PlantUML server
2019-05-09 22:07:35 +02:00
;; version 1.2.11, 2019-04-09 Added `plantuml-download-jar'
;; version 1.2.10, 2019-04-03 Avoid messing with window layouts and buffers -- courtesy of https://github.com/wailo
2019-03-16 12:58:06 +01:00
;; version 1.2.9, Revamped indentation support, now working with a greater number of keywords
2019-01-07 22:25:02 +01:00
;; version 1.2.8, 2019-01-07 Support indentation for activate / deactivate blocks; allow customization of `plantuml-java-args'
2018-08-16 12:08:10 +02:00
;; version 1.2.7, 2018-08-15 Added support for indentation; Fixed the comiling error when installing with melpa
2018-07-17 09:03:00 +02:00
;; version 1.2.6, 2018-07-17 Introduced custom variable `plantuml-jar-args' to control which arguments are passed to PlantUML jar. Fix the warning of failing to specify types of 'defcustom' variables
2017-08-19 19:31:30 +02:00
;; version 1.2.5, 2017-08-19 #53 Fixed installation warnings
;; version 1.2.4, 2017-08-18 #60 Licensed with GPLv3+ to be compatible with Emacs
2016-12-25 20:42:24 +01:00
;; version 1.2.3, 2016-12-25 #50 unicode support in generated output
2016-11-11 11:05:24 +01:00
;; version 1.2.2, 2016-11-11 Fixed java commands handling under windows; support spaces in `plantuml-jar-path'
2016-11-11 09:19:47 +01:00
;; version 1.2.1, 2016-11-11 Support for paths like `~/.plantuml/plantuml.jar' for `plantuml-jar-path' (the tilde was previously unsupported)
2016-11-09 12:21:12 +01:00
;; version 1.2.0, 2016-11-09 Added `plantuml-preview-current-buffer', courtesy of @7mamu4
2016-11-08 12:33:24 +01:00
;; version 1.1.1, 2016-11-08 Fix process handling with Windows native emacs; better file extention match for autoloading the mode
2016-10-18 13:18:58 +02: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.
2016-10-17 16:27:10 +02:00
;; 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-16 16:57:09 +02: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
2012-06-11 13:31:49 +02:00
2015-07-13 23:04:33 +02:00
;;; Code:
2012-06-11 13:31:49 +02:00
( require 'thingatpt )
2019-03-16 10:53:17 +01:00
( require 'dash )
2019-05-09 22:04:51 +02:00
( require 'xml )
2012-06-11 13:31:49 +02:00
2016-10-10 20:30:00 +02:00
( defgroup plantuml-mode nil
2012-06-11 13:31:49 +02:00
" Major mode for editing plantuml file. "
:group 'languages )
2016-10-10 20:30:00 +02:00
( defcustom plantuml-jar-path
2015-07-14 17:02:09 +02:00
( expand-file-name " ~/plantuml.jar " )
2018-07-17 09:03:00 +02:00
" The location of the PlantUML executable JAR. "
:type 'string
:group 'plantuml )
2012-06-11 13:31:49 +02:00
2019-08-19 14:10:35 +02:00
( defcustom plantuml-executable-path
" plantuml "
" The location of the PlantUML executable. "
:type 'string
:group 'plantuml )
2016-10-10 20:30:00 +02:00
( defvar plantuml-mode-hook nil " Standard hook for plantuml-mode. " )
2012-06-11 13:31:49 +02:00
2019-08-22 16:03:53 +02:00
( defconst plantuml-mode-version " 20190822.1403 " " The plantuml-mode version string. " )
2012-06-11 13:31:49 +02:00
2016-10-10 20:30:00 +02:00
( defvar plantuml-mode-debug-enabled nil )
2015-12-07 16:45:40 +01:00
2016-10-10 20:30:00 +02:00
( defvar plantuml-font-lock-keywords nil )
2015-12-07 17:06:19 +01:00
2016-10-10 20:30:00 +02:00
( defvar plantuml-mode-map
2016-10-10 19:42:40 +02:00
( let ( ( keymap ( make-sparse-keymap ) ) )
2016-10-10 20:30:00 +02:00
( define-key keymap ( kbd " C-c C-c " ) 'plantuml-preview )
2015-09-26 11:41:33 +02:00
keymap )
2016-10-10 20:30:00 +02:00
" Keymap for plantuml-mode. " )
2012-06-11 13:31:49 +02:00
2016-10-18 11:38:28 +02:00
( defcustom plantuml-java-command " java "
2018-07-17 09:03:00 +02:00
" The java command used to execute PlantUML. "
:type 'string
:group 'plantuml )
2016-10-14 15:37:02 +02:00
2019-08-21 14:18:20 +02:00
( defcustom plantuml-java-args ( list " -Djava.awt.headless=true " " -jar " " --illegal-access=deny " )
2019-01-07 21:15:41 +01:00
" The parameters passed to `plantuml-java-command' when executing PlantUML. "
:type ' ( repeat string )
:group 'plantuml )
2018-07-17 09:03:00 +02:00
2019-01-07 21:15:41 +01:00
( defcustom plantuml-jar-args ( list " -charset " " UTF-8 " )
" The parameters passed to `plantuml.jar' , when executing PlantUML. "
:type ' ( repeat string )
:group 'plantuml )
2016-10-14 21:37:53 +02:00
2019-05-31 10:53:40 +02:00
( defcustom plantuml-server-url " https://www.plantuml.com/plantuml "
" The base URL of the PlantUML server. "
:type 'string
:group 'plantuml )
2019-08-19 14:10:35 +02:00
( defcustom plantuml-executable-args ( list " -headless " )
" The parameters passed to plantuml executable when executing PlantUML. "
:type ' ( repeat string )
:group 'plantuml )
2019-05-31 10:53:40 +02:00
( defcustom plantuml-default-exec-mode 'server
" Default execution mode for PlantUML. Valid values are:
- ` jar ' : run PlantUML as a JAR file ( requires a local install of the PlantUML JAR file, see ` plantuml-jar-path ' "
:type 'symbol
:group 'plantuml
2019-08-19 14:10:35 +02:00
:options ' ( jar server executable ) )
2019-05-31 10:53:40 +02:00
2016-10-18 11:38:28 +02:00
( defcustom plantuml-suppress-deprecation-warning t
2018-07-17 09:03:00 +02:00
" To silence the deprecation warning when `puml-mode' is found upon loading. "
:type 'boolean
:group 'plantuml )
2016-10-18 11:38:28 +02:00
2019-05-31 10:53:40 +02:00
( defun plantuml-jar-render-command ( &rest arguments )
2016-10-10 20:05:27 +02:00
" Create a command line to execute PlantUML with arguments (as ARGUMENTS). "
2018-07-17 09:03:00 +02:00
( let* ( ( cmd-list ( append plantuml-java-args ( list ( expand-file-name plantuml-jar-path ) ) plantuml-jar-args arguments ) )
2016-11-08 12:33:24 +01:00
( cmd ( mapconcat 'identity cmd-list " | " ) ) )
2016-10-22 21:15:22 +02:00
( plantuml-debug ( format " Command is [%s] " cmd ) )
cmd-list ) )
2016-10-10 19:42:40 +02:00
2012-06-11 13:31:49 +02:00
;;; syntax table
2016-10-10 20:30:00 +02:00
( defvar plantuml-mode-syntax-table
2012-06-11 13:31:49 +02:00
( let ( ( synTable ( make-syntax-table ) ) )
2016-10-17 22:12:27 +02:00
( modify-syntax-entry ?\/ " . 14c " synTable )
( modify-syntax-entry ?' " < 23 " synTable )
2015-12-12 16:45:43 +01: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 )
2012-06-11 13:31:49 +02:00
synTable )
2016-10-10 20:30:00 +02:00
" Syntax table for `plantuml-mode' . " )
2012-06-11 13:31:49 +02:00
2016-10-10 20:30:00 +02:00
( defvar plantuml-types nil )
( defvar plantuml-keywords nil )
( defvar plantuml-preprocessors nil )
( defvar plantuml-builtins nil )
2012-06-11 13:31:49 +02:00
;; keyword completion
2016-10-10 20:30:00 +02:00
( defvar plantuml-kwdList nil " The plantuml keywords. " )
2012-06-11 13:31:49 +02:00
2019-05-31 10:53:40 +02:00
;; PlantUML execution mode
2019-08-12 17:40:00 +02:00
( defvar-local plantuml-exec-mode nil
" The Plantuml execution mode override. See `plantuml-default-exec-mode' for acceptable values. " )
2019-05-31 10:53:40 +02:00
( defun plantuml-set-exec-mode ( mode )
" Set the execution mode MODE for PlantUML. "
( interactive ( let* ( ( completion-ignore-case t )
2019-08-19 14:10:35 +02:00
( supported-modes ' ( " jar " " server " " executable " ) ) )
2019-08-03 15:47:00 +02:00
( list ( completing-read ( format " Exec mode [%s]: " plantuml-exec-mode )
2019-08-21 11:29:20 +02:00
supported-modes
nil
t
nil
nil
plantuml-exec-mode ) ) ) )
2019-08-19 14:10:35 +02:00
( if ( member mode ' ( " jar " " server " " executable " ) )
2019-05-31 10:53:40 +02:00
( setq plantuml-exec-mode ( intern mode ) )
( error ( concat " Unsupported mode: " mode ) ) ) )
2019-08-12 17:40:00 +02:00
( defun plantuml-get-exec-mode ( )
" Retrieves the currently active PlantUML exec mode. "
( or plantuml-exec-mode
plantuml-default-exec-mode ) )
2016-10-10 20:30:00 +02:00
( defun plantuml-enable-debug ( )
" Enables debug messages into the *PLANTUML Messages* buffer. "
2015-12-07 16:49:21 +01:00
( interactive )
2016-10-10 20:30:00 +02:00
( setq plantuml-mode-debug-enabled t ) )
2015-12-07 16:45:40 +01:00
2016-10-10 20:30:00 +02:00
( defun plantuml-disable-debug ( )
" Stops any debug messages to be added into the *PLANTUML Messages* buffer. "
2015-12-07 16:49:21 +01:00
( interactive )
2016-10-10 20:30:00 +02:00
( setq plantuml-mode-debug-enabled nil ) )
2015-12-07 16:45:40 +01:00
2016-10-10 20:30:00 +02:00
( 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* " )
2015-12-07 16:45:40 +01:00
( 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
2019-05-09 22:04:51 +02:00
( defun plantuml-download-jar ( )
" Download the latest PlantUML JAR file and install it into `plantuml-jar-path' . "
( interactive )
( if ( y-or-n-p ( format " Download the latest PlantUML JAR file into %s? " plantuml-jar-path ) )
( if ( or ( not ( file-exists-p plantuml-jar-path ) )
( y-or-n-p ( format " The PlantUML jar file already exists at %s, overwrite? " plantuml-jar-path ) ) )
( with-current-buffer ( url-retrieve-synchronously " https://search.maven.org/solrsearch/select?q=g:net.sourceforge.plantuml+AND+a:plantuml&core=gav&start=0&rows=1&wt=xml " )
( mkdir ( file-name-directory plantuml-jar-path ) t )
( let* ( ( parse-tree ( xml-parse-region ) )
( doc ( ->> parse-tree
( assq 'response )
( assq 'result )
( assq 'doc ) ) )
( strs ( xml-get-children doc 'str ) )
( version ( ->> strs
( --filter ( string-equal " v " ( xml-get-attribute it 'name ) ) )
2019-08-21 11:29:20 +02:00
( car )
2019-05-09 22:04:51 +02:00
( xml-node-children )
2019-08-21 11:29:20 +02:00
( car ) ) ) )
2019-05-09 22:04:51 +02:00
( message ( concat " Downloading PlantUML v " version " into " plantuml-jar-path ) )
2019-05-31 10:53:40 +02:00
( url-copy-file ( format " https://search.maven.org/remotecontent?filepath=net/sourceforge/plantuml/plantuml/%s/plantuml-%s.jar " version version ) plantuml-jar-path t )
( kill-buffer ) ) )
( message " Aborted. " ) )
2019-05-09 22:04:51 +02:00
( message " Aborted. " ) ) )
2019-05-31 10:53:40 +02:00
( defun plantuml-jar-get-language ( buf )
" Retrieve the language specification from the PlantUML JAR file and paste it into BUF. "
2016-10-22 21:15:22 +02:00
( unless ( or ( eq system-type 'cygwin ) ( file-exists-p plantuml-jar-path ) )
2016-10-10 20:30:00 +02:00
( error " Could not find plantuml.jar at %s " plantuml-jar-path ) )
2019-05-31 10:53:40 +02:00
( with-current-buffer buf
2016-10-22 21:15:22 +02:00
( let ( ( cmd-args ( append ( list plantuml-java-command nil t nil )
2019-05-31 10:53:40 +02:00
( plantuml-jar-render-command " -language " ) ) ) )
2016-10-22 21:15:22 +02:00
( apply 'call-process cmd-args )
2019-05-31 10:53:40 +02:00
( goto-char ( point-min ) ) ) ) )
( defun plantuml-server-get-language ( buf )
" Retrieve the language specification from the PlantUML server and paste it into BUF. "
( let ( ( lang-url ( concat plantuml-server-url " /language " ) ) )
( with-current-buffer buf
( url-insert-file-contents lang-url ) ) ) )
2019-08-19 14:10:35 +02:00
( defun plantuml-executable-get-language ( buf )
" Retrieve the language specification from the PlantUML executable and paste it into BUF. "
( with-current-buffer buf
( let ( ( cmd-args ( append ( list plantuml-executable-path nil t nil ) ( list " -language " ) ) ) )
( apply 'call-process cmd-args )
( goto-char ( point-min ) ) ) ) )
2019-05-31 10:53:40 +02:00
( defun plantuml-get-language ( mode buf )
" Retrieve the language spec using the preferred PlantUML execution mode MODE. Paste the result into BUF. "
( let ( ( get-fn ( pcase mode
( 'jar #' plantuml-jar-get-language )
2019-08-19 14:10:35 +02:00
( 'server #' plantuml-server-get-language )
( 'executable #' plantuml-executable-get-language ) ) ) )
2019-05-31 10:53:40 +02:00
( if get-fn
( funcall get-fn buf )
( error " Unsupported execution mode %s " mode ) ) ) )
( defun plantuml-init ( mode )
" Initialize the keywords or builtins from the cmdline language output. Use exec mode MODE to load the language details. "
( with-temp-buffer
( plantuml-get-language mode ( current-buffer ) )
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 )
2016-10-10 20:05:27 +02:00
;; else
( forward-line )
( setq count ( string-to-number ( current-word ) ) )
( beginning-of-line 2 )
( setq pos ( point ) )
( forward-line count )
( cond ( ( string= word " type " )
2016-10-10 20:30:00 +02:00
( setq plantuml-types
2016-10-10 20:05:27 +02:00
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
( ( string= word " keyword " )
2016-10-10 20:30:00 +02:00
( setq plantuml-keywords
2016-10-10 20:05:27 +02:00
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
( ( string= word " preprocessor " )
2016-10-10 20:30:00 +02:00
( setq plantuml-preprocessors
2016-10-10 20:05:27 +02:00
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) )
2016-10-10 20:30:00 +02:00
( t ( setq plantuml-builtins
2016-10-10 20:05:27 +02:00
( append
2016-10-10 20:30:00 +02:00
plantuml-builtins
2016-10-10 20:05:27 +02:00
( split-string
( buffer-substring-no-properties pos ( point ) ) ) ) ) ) )
( setq found ( search-forward " ; " nil nil ) ) ) ) ) ) )
2012-06-11 13:31:49 +02:00
2016-10-10 20:30:00 +02:00
( defconst plantuml-preview-buffer " *PLANTUML Preview* " )
2015-09-14 12:04:32 +02:00
2016-10-10 20:30:00 +02:00
( defvar plantuml-output-type
2015-09-26 12:42:34 +02:00
( if ( not ( display-images-p ) )
2019-05-31 10:53:40 +02:00
" txt "
2015-09-26 12:42:34 +02:00
( cond ( ( image-type-available-p 'svg ) " svg " )
2016-01-24 21:34:23 +01:00
( ( image-type-available-p 'png ) " png " )
2019-05-31 10:53:40 +02:00
( t " txt " ) ) )
2015-09-26 12:39:16 +02:00
" Specify the desired output type to use for generated diagrams. " )
2016-10-10 20:30:00 +02:00
( defun plantuml-read-output-type ( )
2015-09-26 12:39:16 +02:00
" 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 " ) )
2019-05-31 10:53:40 +02:00
' ( " txt " ) ) ) )
2016-10-10 20:30:00 +02:00
( completing-read ( format " Output type [%s]: " plantuml-output-type )
2015-09-26 12:39:16 +02:00
available-types
nil
t
nil
nil
2016-10-10 20:30:00 +02:00
plantuml-output-type ) ) )
2015-09-26 12:39:16 +02:00
2016-10-10 20:30:00 +02:00
( defun plantuml-set-output-type ( type )
2015-12-07 17:06:19 +01:00
" Set the desired output type (as TYPE) for the current buffer.
2015-12-07 17:43:36 +01:00
If the
2016-10-10 20:30:00 +02:00
major mode of the current buffer mode is not plantuml-mode, set the
2015-09-26 12:39:16 +02:00
default output type for new buffers. "
2016-10-10 20:30:00 +02:00
( interactive ( list ( plantuml-read-output-type ) ) )
( setq plantuml-output-type type ) )
2015-09-26 12:39:16 +02:00
2016-10-10 20:30:00 +02:00
( defun plantuml-is-image-output-p ( )
2019-05-31 10:53:40 +02:00
" Return non-nil if the diagram output format is an image, false if it's text based. "
( not ( equal " txt " plantuml-output-type ) ) )
2015-09-21 17:03:33 +02:00
2019-05-31 10:53:40 +02:00
( defun plantuml-jar-output-type-opt ( output-type )
" Create the flag to pass to PlantUML according to OUTPUT-TYPE.
Note that output type ` txt ' is promoted to ` utxt ' for better rendering. "
( concat " -t " ( pcase output-type
( " txt " " utxt " )
( _ output-type ) ) ) )
2015-09-14 12:04:32 +02:00
2019-05-31 10:53:40 +02:00
( defun plantuml-jar-start-process ( buf )
2016-10-18 11:38:28 +02:00
" Run PlantUML as an Emacs process and puts the output into the given buffer (as BUF). "
2019-01-07 21:15:41 +01:00
( apply #' start-process
" PLANTUML " buf plantuml-java-command
` ( ,@ plantuml-java-args
, ( expand-file-name plantuml-jar-path )
2019-05-31 10:53:40 +02:00
, ( plantuml-jar-output-type-opt plantuml-output-type )
2019-01-07 21:15:41 +01:00
,@ plantuml-jar-args
" -p " ) ) )
2016-10-18 11:38:28 +02:00
2019-08-19 14:10:35 +02:00
( defun plantuml-executable-start-process ( buf )
" Run PlantUML as an Emacs process and puts the output into the given buffer (as BUF). "
( apply #' start-process
" PLANTUML " buf plantuml-executable-path
` ( ,@ plantuml-executable-args
, ( plantuml-jar-output-type-opt plantuml-output-type )
" -p " ) ) )
2019-05-31 10:53:40 +02:00
( defun plantuml-update-preview-buffer ( prefix buf )
" Show the preview in the preview buffer BUF.
Window is selected according to PREFIX:
- 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 ( ( imagep ( and ( display-images-p )
( plantuml-is-image-output-p ) ) ) )
( cond
( ( = prefix 16 ) ( switch-to-buffer-other-frame buf ) )
( ( = prefix 4 ) ( switch-to-buffer-other-window buf ) )
( t ( display-buffer buf ) ) )
( when imagep
( with-current-buffer buf
( image-mode )
( set-buffer-multibyte t ) ) ) ) )
( defun plantuml-jar-preview-string ( prefix string buf )
" Preview the diagram from STRING by running the PlantUML JAR.
Put the result into buffer BUF. Window is selected according to PREFIX:
- 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* ( ( process-connection-type nil )
( ps ( plantuml-jar-start-process buf ) ) )
( 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 ) )
( plantuml-update-preview-buffer prefix buf ) ) ) ) )
2019-08-28 12:37:09 +02:00
( defun plantuml-server-encode-url ( string )
" Encode the string STRING into a URL suitable for PlantUML server interactions. "
( let* ( ( coding-system ( or buffer-file-coding-system
" utf8 " ) )
( encoded-string ( base64-encode-string ( encode-coding-string string coding-system ) ) ) )
( concat plantuml-server-url " / " plantuml-output-type " /-base64- " encoded-string ) ) )
2019-05-31 10:53:40 +02:00
( defun plantuml-server-preview-string ( prefix string buf )
" Preview the diagram from STRING as rendered by the PlantUML server.
Put the result into buffer BUF and place it according to PREFIX:
2016-03-24 23:25:51 +01:00
- 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 "
2019-08-28 12:37:09 +02:00
( let* ( ( url-request-location ( plantuml-server-encode-url string ) ) )
2019-05-31 10:53:40 +02:00
( save-current-buffer
( save-match-data
( url-retrieve url-request-location
( lambda ( status )
;; TODO: error check
( goto-char ( point-min ) )
;; skip the HTTP headers
( while ( not ( looking-at " \n " ) )
( forward-line ) )
( kill-region ( point-min ) ( + 1 ( point ) ) )
( copy-to-buffer buf ( point-min ) ( point-max ) )
( plantuml-update-preview-buffer prefix buf ) ) ) ) ) ) )
2019-08-19 14:10:35 +02:00
( defun plantuml-executable-preview-string ( prefix string buf )
" Preview the diagram from STRING by running the PlantUML JAR.
Put the result into buffer BUF. Window is selected according to PREFIX:
- 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* ( ( process-connection-type nil )
( ps ( plantuml-executable-start-process buf ) ) )
( 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 ) )
( plantuml-update-preview-buffer prefix buf ) ) ) ) )
2019-05-31 10:53:40 +02:00
( defun plantuml-exec-mode-preview-string ( prefix mode string buf )
" Preview the diagram from STRING using the execution mode MODE.
Put the result into buffer BUF, selecting the window according to PREFIX:
- 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 ( ( preview-fn ( pcase mode
( 'jar #' plantuml-jar-preview-string )
2019-08-19 14:10:35 +02:00
( 'server #' plantuml-server-preview-string )
( 'executable #' plantuml-executable-preview-string ) ) ) )
2019-05-31 10:53:40 +02:00
( if preview-fn
( funcall preview-fn prefix string buf )
( error " Unsupported execution mode %s " mode ) ) ) )
( defun plantuml-preview-string ( prefix string )
" Preview diagram from PlantUML sources (as STRING), using prefix (as PREFIX)
to choose where to display it. "
2016-10-10 20:30:00 +02:00
( let ( ( b ( get-buffer plantuml-preview-buffer ) ) )
2015-09-14 12:04:32 +02:00
( when b
( kill-buffer b ) ) )
2015-12-07 17:43:36 +01:00
2015-09-26 13:05:00 +02:00
( let* ( ( imagep ( and ( display-images-p )
2016-10-10 20:30:00 +02:00
( plantuml-is-image-output-p ) ) )
( buf ( get-buffer-create plantuml-preview-buffer ) )
2015-09-26 13:05:00 +02:00
( coding-system-for-read ( and imagep 'binary ) )
2015-12-07 17:43:36 +01:00
( coding-system-for-write ( and imagep 'binary ) ) )
2019-08-12 17:40:00 +02:00
( plantuml-exec-mode-preview-string prefix ( plantuml-get-exec-mode ) string buf ) ) )
2015-09-14 12:04:32 +02:00
2016-10-10 20:30:00 +02:00
( defun plantuml-preview-buffer ( prefix )
2016-10-10 20:05:27 +02:00
" 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 " )
2016-10-10 20:30:00 +02:00
( plantuml-preview-string prefix ( buffer-string ) ) )
2016-10-10 20:05:27 +02:00
2016-11-12 01:48:47 +01:00
( defun plantuml-preview-region ( prefix begin end )
" Preview diagram from the PlantUML sources in from BEGIN to END.
Uses the current region when called interactively.
2016-10-10 20:05:27 +02:00
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 "
2016-11-12 01:48:47 +01:00
( interactive " p \n r " )
2016-10-10 20:30:00 +02:00
( plantuml-preview-string prefix ( concat " @startuml \n "
2018-08-15 10:59:25 +02:00
( buffer-substring-no-properties
begin end )
" \n @enduml " ) ) )
2016-10-10 20:05:27 +02:00
2016-10-26 01:03:37 +02:00
( defun plantuml-preview-current-block ( prefix )
" Preview diagram from the PlantUML sources from the previous @startuml to the next @enduml.
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 " )
( save-restriction
( narrow-to-region
( search-backward " @startuml " ) ( search-forward " @enduml " ) )
( plantuml-preview-buffer prefix ) ) )
2016-10-10 20:30:00 +02:00
( defun plantuml-preview ( prefix )
2016-10-10 20:05:27 +02:00
" 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 " )
2016-10-14 00:20:25 +02:00
( if mark-active
2016-11-12 01:48:47 +01:00
( plantuml-preview-region prefix ( region-beginning ) ( region-end ) )
2018-08-15 10:59:25 +02:00
( plantuml-preview-buffer prefix ) ) )
2016-10-10 20:05:27 +02:00
2019-05-31 10:53:40 +02:00
( defun plantuml-init-once ( &optional mode )
2019-08-12 17:40:00 +02:00
" Ensure initialization only happens once. Use exec mode MODE to load the language details or by first querying `plantuml-get-exec-mode' . "
( let ( ( mode ( or mode ( plantuml-get-exec-mode ) ) ) )
2019-05-31 10:53:40 +02:00
( unless plantuml-kwdList
( plantuml-init mode )
( 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 ) ) )
( defvar plantuml-indent-regexp-block-start " ^.*{ \s *$ "
" Indentation regex for all plantuml elements that might define a {} block.
2019-03-16 10:53:17 +01:00
Plantuml elements like skinparam, rectangle, sprite, package, etc.
The opening { has to be the last visible character in the line ( whitespace
might follow ) . " )
2019-05-31 10:53:40 +02:00
( defvar plantuml-indent-regexp-note-start " ^ \s * \\ (floating \s + \\ )?[hr]?note[^:]*?$ " " simplyfied regex; note syntax is especially inconsistent across diagrams " )
( defvar plantuml-indent-regexp-group-start " ^ \s * \\ (alt \\ |else \\ |opt \\ |loop \\ |par \\ |break \\ |critical \\ |group \\ ) \\ (?: \s +.+ \\ |$ \\ ) "
" Indentation regex for plantuml group elements that are defined for sequence diagrams.
2019-03-16 10:53:17 +01:00
Two variants for groups: keyword is either followed by whitespace and some text
or it is followed by line end. " )
2019-05-31 10:53:40 +02:00
( defvar plantuml-indent-regexp-activate-start " ^ \s *activate \s +.+$ " )
( defvar plantuml-indent-regexp-box-start " ^ \s *box \s +.+$ " )
( defvar plantuml-indent-regexp-ref-start " ^ref \s +over \s +[^:]+?$ " )
( defvar plantuml-indent-regexp-title-start " ^ \s *title$ " )
( defvar plantuml-indent-regexp-header-start " ^ \s * \\ (?: \\ (?:center \\ |left \\ |right \\ ) \s +header \\ |header \\ )$ " )
( defvar plantuml-indent-regexp-footer-start " ^ \s * \\ (?: \\ (?:center \\ |left \\ |right \\ ) \s +footer \\ |footer \\ )$ " )
( defvar plantuml-indent-regexp-legend-start " ^ \s * \\ (?:legend \\ |legend \s + \\ (?:bottom \\ |top \\ ) \\ |legend \s + \\ (?:center \\ |left \\ |right \\ ) \\ |legend \s + \\ (?:bottom \\ |top \\ ) \s + \\ (?:center \\ |left \\ |right \\ ) \\ )$ " )
( defvar plantuml-indent-regexp-oldif-start " ^.*if \s + \" .* \" \s +then$ " " used in current activity diagram, sometimes already mentioned as deprecated " )
( defvar plantuml-indent-regexp-macro-start " ^ \s *!definelong.*$ " )
( defvar plantuml-indent-regexp-start ( list plantuml-indent-regexp-block-start
plantuml-indent-regexp-group-start
plantuml-indent-regexp-activate-start
plantuml-indent-regexp-box-start
plantuml-indent-regexp-ref-start
plantuml-indent-regexp-legend-start
plantuml-indent-regexp-note-start
plantuml-indent-regexp-oldif-start
plantuml-indent-regexp-title-start
plantuml-indent-regexp-header-start
plantuml-indent-regexp-footer-start
plantuml-indent-regexp-macro-start
plantuml-indent-regexp-oldif-start ) )
( defvar plantuml-indent-regexp-end " ^ \s * \\ (?:} \\ |endif \\ |else \s *.* \\ |end \\ |end \s +note \\ |endhnote \\ |endrnote \\ |end \s +box \\ |end \s +ref \\ |deactivate \s +.+ \\ |end \s +title \\ |endheader \\ |endfooter \\ |endlegend \\ |!enddefinelong \\ )$ " )
( 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-10 20:30:00 +02:00
( defun plantuml-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 " " ) )
2016-10-10 20:30:00 +02:00
( setq maxMatchResult ( try-completion meat 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
2016-10-10 20:30:00 +02:00
( all-completions meat plantuml-kwdList ) ) )
2012-06-11 13:31:49 +02:00
( message " Making completion list...%s " " done " ) ) ) ) )
2018-08-14 23:08:29 +02:00
;; indentation
2018-08-16 11:32:44 +02:00
( defun plantuml-current-block-depth ( )
" Trace the current block indentation level by recursively looking back line by line. "
2018-08-14 23:08:29 +02:00
( save-excursion
2019-01-07 21:23:04 +01:00
( let ( ( relative-depth 0 ) )
;; current line
2018-08-16 11:32:44 +02:00
( beginning-of-line )
2019-01-07 21:23:04 +01:00
( if ( looking-at plantuml-indent-regexp-end )
( setq relative-depth ( 1- relative-depth ) ) )
;; from current line backwards to beginning of buffer
( while ( not ( bobp ) )
( forward-line -1 )
2018-08-14 23:08:29 +02:00
( if ( looking-at plantuml-indent-regexp-end )
2018-08-16 11:32:44 +02:00
( setq relative-depth ( 1- relative-depth ) ) )
2019-03-16 10:53:17 +01:00
( if ( -any? 'looking-at plantuml-indent-regexp-start )
2019-01-07 21:23:04 +01:00
( setq relative-depth ( 1+ relative-depth ) ) ) )
2018-08-14 23:08:29 +02:00
2018-08-16 11:32:44 +02:00
( if ( <= relative-depth 0 )
2018-08-14 23:08:29 +02:00
0
2018-08-16 11:32:44 +02:00
relative-depth ) ) ) )
2018-08-14 23:08:29 +02:00
( defun plantuml-indent-line ( )
2019-01-07 21:23:04 +01:00
" Indent the current line to its desired indentation level.
Restore point to same position in text of the line as before indentation. "
2018-08-14 23:08:29 +02:00
( interactive )
2019-01-07 21:23:04 +01:00
;; store position of point in line measured from end of line
( let ( ( original-position-eol ( - ( line-end-position ) ( point ) ) ) )
2018-08-16 11:32:44 +02:00
( save-excursion
( beginning-of-line )
2019-01-07 21:23:04 +01:00
( indent-line-to ( * tab-width ( plantuml-current-block-depth ) ) ) )
;; restore position in text of line
( goto-char ( - ( line-end-position ) original-position-eol ) ) ) )
2018-08-14 23:08:29 +02:00
2016-07-08 04:32:08 +02:00
;;;###autoload
2016-10-22 16:13:52 +02:00
( add-to-list 'auto-mode-alist ' ( " \\ . \\ (plantuml \\ |pum \\ |plu \\ ) \\ ' " . plantuml-mode ) )
2012-06-11 13:31:49 +02:00
;;;###autoload
2016-10-10 20:30:00 +02:00
( define-derived-mode plantuml-mode prog-mode " plantuml "
2012-06-11 13:31:49 +02:00
" Major mode for plantuml.
Shortcuts Command Name
2016-10-10 20:30:00 +02:00
\\ [ plantuml-complete-symbol ] ` plantuml-complete-symbol ' "
2016-10-16 20:37:20 +02:00
( plantuml-init-once )
2016-10-10 20:30:00 +02:00
( make-local-variable 'plantuml-output-type )
2015-12-12 16:45:43 +01:00
( 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 )
2018-08-14 23:08:29 +02:00
( set ( make-local-variable 'indent-line-function ) 'plantuml-indent-line )
2016-10-17 08:39:34 +02:00
( setq font-lock-defaults ' ( ( plantuml-font-lock-keywords ) nil t ) ) )
2016-10-10 20:25:07 +02:00
2016-10-14 21:27:20 +02:00
( defun plantuml-deprecation-warning ( )
" Warns the user about the deprecation of the `puml-mode' project. "
2016-10-14 21:37:53 +02:00
( if ( and plantuml-suppress-deprecation-warning
( featurep 'puml-mode ) )
2016-10-14 21:27:20 +02:00
( display-warning :warning
2019-08-12 17:14:45 +02:00
" `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 '. \
2016-10-14 21:27:20 +02:00
See more at https://github.com/skuro/puml-mode/issues/26 " )))
( add-hook 'plantuml-mode-hook 'plantuml-deprecation-warning )
2016-10-10 20:30:00 +02:00
( provide 'plantuml-mode )
;;; plantuml-mode.el ends here