2015-09-26 13:05:00 +02:00
;;; puml-mode.el --- Major mode for PlantUML -*- lexical-binding: t; -*-
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
2016-06-05 18:09:35 +02:00
;; Version: 0.6.5
2016-07-08 07:09:40 +02:00
;; Package-Requires: ((emacs "24"))
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:
;;
2016-10-10 20:25:07 +02:00
;; Version 0.6.7, 2016-10-11 Added deprecation warning in favor of plantuml-mode
2016-07-19 09:36:39 +02:00
;; version 0.6.6, 2016-07-19 Added autoload, minor bug fixes
2016-03-24 22:34:50 +01:00
;; version 0.6.5, 2016-03-24 Added UTF8 support and open in new window / frame shortcuts
2015-12-12 17:18:24 +01:00
;; version 0.6.4, 2015-12-12 Added support for comments (single and multiline) -- thanks to https://github.com/nivekuil
2015-12-07 17:43:36 +01:00
;; version 0.6.3, 2015-11-07 Added per-buffer configurability of output type (thanks to https://github.com/davazp)
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
2016-10-10 20:25:07 +02:00
( defconst puml-mode-version " 0.6.7 " " 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-12-07 17:06:19 +01:00
( defvar puml-font-lock-keywords nil )
2015-09-26 11:41:33 +02:00
( defvar puml-mode-map
2016-10-10 19:42:40 +02:00
( let ( ( keymap ( make-sparse-keymap ) ) )
2015-09-26 11:41:33 +02:00
( define-key keymap ( kbd " C-c C-c " ) 'puml-preview )
keymap )
" Keymap for puml-mode. " )
2012-06-11 13:31:49 +02:00
2016-10-10 19:42:40 +02:00
( defvar puml-run-command " java -jar %s " )
( defun puml-render-command ( &rest arguments )
2016-10-10 20:05:27 +02:00
" Create a command line to execute PlantUML with arguments (as ARGUMENTS). "
2016-10-10 19:42:40 +02:00
;; (shell-command (format "") (concat plantuml-run-command " " buffer-file-name))
( let ( ( cmd ( format puml-run-command ( shell-quote-argument puml-plantuml-jar-path ) ) )
( argstring ( mapconcat 'shell-quote-argument arguments " " ) ) )
( concat cmd " " argstring ) ) )
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 ) ) )
2015-12-12 17:18:24 +01:00
( modify-syntax-entry ?\/ " . 41 " synTable )
2015-12-12 17:23:02 +01:00
( modify-syntax-entry ?' " ! 23b " 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 )
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. "
2016-10-10 20:25:07 +02:00
( display-warning :warning " `puml-mode' is deprecated in favor of `plantuml-mode' , make sure to update your configuration " )
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
2016-10-10 19:42:40 +02:00
( shell-command ( puml-render-command " -charset UTF-8 -language " ) ( current-buffer ) )
2012-06-11 13:31:49 +02:00
( 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 )
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 " )
( 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 ) ) ) ) ) ) )
( setq found ( search-forward " ; " nil nil ) ) ) ) ) ) )
2012-06-11 13:31:49 +02:00
2015-09-14 12:04:32 +02:00
( defconst puml-preview-buffer " *PUML Preview* " )
2015-09-26 12:39:16 +02:00
( defvar puml-output-type
2015-09-26 12:42:34 +02:00
( if ( not ( display-images-p ) )
" utxt "
( cond ( ( image-type-available-p 'svg ) " svg " )
2016-01-24 21:34:23 +01:00
( ( image-type-available-p 'png ) " png " )
( t " utxt " ) ) )
2015-09-26 12:39:16 +02:00
" Specify the desired output type to use for generated diagrams. " )
( defun puml-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]: " puml-output-type )
available-types
nil
t
nil
nil
puml-output-type ) ) )
( defun puml-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
major mode of the current buffer mode is not puml-mode, set the
2015-09-26 12:39:16 +02:00
default output type for new buffers. "
( interactive ( list ( puml-read-output-type ) ) )
( setq puml-output-type type ) )
2015-09-21 17:03:33 +02:00
( 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 12:39:16 +02:00
( not ( equal " 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. "
2015-09-26 12:39:16 +02:00
( concat " -t " puml-output-type ) )
2015-09-14 12:04:32 +02:00
2016-10-10 20:05:27 +02:00
( defun puml-preview-string ( prefix string )
" Preview diagram from PlantUML sources (as STRING), using prefix (as PREFIX)
to choose where to display it:
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 "
2015-09-14 12:04:32 +02:00
( let ( ( b ( get-buffer puml-preview-buffer ) ) )
( 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 )
( puml-is-image-output-p ) ) )
( process-connection-type nil )
( buf ( get-buffer-create puml-preview-buffer ) )
( coding-system-for-read ( and imagep 'binary ) )
2015-12-07 17:43:36 +01:00
( coding-system-for-write ( and imagep 'binary ) ) )
2015-09-14 12:04:32 +02:00
( let ( ( ps ( start-process " PUML " buf
2015-12-07 17:43:36 +01:00
" java " " -jar " ( shell-quote-argument puml-plantuml-jar-path )
2015-09-14 12:04:32 +02:00
( puml-output-type-opt ) " -p " ) ) )
2016-10-10 20:05:27 +02:00
( process-send-string ps string )
2015-09-14 12:04:32 +02:00
( process-send-eof ps )
2015-09-26 13:05:00 +02:00
( set-process-sentinel ps
2016-07-08 04:31:06 +02:00
( lambda ( _ps event )
2015-09-26 13:05:00 +02:00
( unless ( equal event " finished \n " )
( error " PUML Preview failed: %s " event ) )
2016-03-21 16:32:17 +01:00
( cond
( ( = prefix 16 )
( switch-to-buffer-other-frame puml-preview-buffer ) )
( ( = prefix 4 )
( switch-to-buffer-other-window puml-preview-buffer ) )
( t ( switch-to-buffer puml-preview-buffer ) ) )
2015-09-26 13:05:00 +02:00
( when imagep
( image-mode )
( set-buffer-multibyte t ) ) ) ) ) ) )
2015-09-14 12:04:32 +02:00
2016-10-10 20:05:27 +02:00
( defun puml-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 " )
( puml-preview-string prefix ( buffer-string ) ) )
( defun puml-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 " )
( puml-preview-string prefix ( concat " @startuml \n "
( buffer-substring-no-properties
( region-beginning ) ( region-end ) )
" \n @enduml " ) ) )
( defun puml-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 )
( puml-preview-region prefix )
( puml-preview-buffer prefix ) ) )
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 " ) ) ) ) )
2016-07-08 04:32:08 +02:00
;;;###autoload
2016-10-10 19:42:40 +02:00
( add-to-list 'auto-mode-alist ' ( " \\ .(plantuml|pum|puml|plu)$ " . 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 12:39:16 +02:00
( make-local-variable 'puml-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 )
2016-10-10 20:25:07 +02:00
( setq font-lock-defaults ' ( ( puml-font-lock-keywords ) nil t ) )
; Run hooks:
( run-mode-hooks 'puml-mode-hook ) )
( defun puml-deprecation-warning ( )
" Warns the user about the deprecation of the `puml-mode' project. "
( display-warning :warning
" `puml-mode' is now deprecated and no longer updated, you should move your configuration to use `plantuml-mode' . See https://github.com/sytac/plantuml-mode. " ) )
( add-hook 'puml-mode-hook 'puml-deprecation-warning )
2012-06-11 13:31:49 +02:00
2015-07-13 23:04:33 +02:00
( provide 'puml-mode )
;;; puml-mode.el ends here