diff --git a/init.el b/init.el index 089d7f3..6620177 100644 --- a/init.el +++ b/init.el @@ -2946,7 +2946,9 @@ Note that this workaround is incomplete, as explained in this comment." 'interactive-haskell-mode))) (use-package plantuml-mode - :load-path "site-lisp" + :vc (:url "https://github.com/exot/plantuml-mode" + :branch "add-file-export-shadow" + :rev :newest) :mode ("\\.plantuml\\'" . plantuml-mode) :commands (plantuml-mode) :init (setq plantuml-output-type "svg" diff --git a/site-lisp/plantuml-mode.el b/site-lisp/plantuml-mode.el deleted file mode 100644 index f8af033..0000000 --- a/site-lisp/plantuml-mode.el +++ /dev/null @@ -1,920 +0,0 @@ -;;; 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 25 through 27 (current master) -;; Author: Zhang Weize (zwz) -;; Maintainer: Carlo Sciolla (skuro) -;; Keywords: uml plantuml ascii -;; Version: 1.2.9 -;; Package-Version: 1.2.9 -;; Package-Requires: ((dash "2.0.0") (emacs "25.0")) - -;; 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 . - -;;; 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 1.4.1, 2019-09-03 Better indentation; more bugfixing; actually adding `executable' mode -;; version 1.4.0, 2019-08-21 Added `executable' exec mode to use locally installed `plantuml' binaries, various bugfixes -;; version 1.3.1, 2019-08-02 Fixed interactive behavior of `plantuml-set-exec-mode' -;; version 1.3.0, 2019-05-31 Added experimental support for multiple rendering modes and, specifically, preview using a PlantUML server -;; 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 -;; version 1.2.9, Revamped indentation support, now working with a greater number of keywords -;; version 1.2.8, 2019-01-07 Support indentation for activate / deactivate blocks; allow customization of `plantuml-java-args' -;; version 1.2.7, 2018-08-15 Added support for indentation; Fixed the comiling error when installing with melpa -;; 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 -;; 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 -;; version 1.2.3, 2016-12-25 #50 unicode support in generated output -;; version 1.2.2, 2016-11-11 Fixed java commands handling under windows; support spaces in `plantuml-jar-path' -;; version 1.2.1, 2016-11-11 Support for paths like `~/.plantuml/plantuml.jar' for `plantuml-jar-path' (the tilde was previously unsupported) -;; version 1.2.0, 2016-11-09 Added `plantuml-preview-current-buffer', courtesy of @7mamu4 -;; version 1.1.1, 2016-11-08 Fix process handling with Windows native emacs; better file extention match for autoloading the mode -;; 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. -;; 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) -(require 'dash) -(require 'xml) -(require 'cl-lib) - -(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." - :type 'string - :group 'plantuml) - -(defcustom plantuml-executable-path - "plantuml" - "The location of the PlantUML executable." - :type 'string - :group 'plantuml) - -(defvar plantuml-mode-hook nil "Standard hook for plantuml-mode.") - -(defconst plantuml-mode-version "20190905.838" "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) - (define-key keymap (kbd "C-c C-e") 'plantuml-export) - (define-key keymap (kbd "C-c C-v") 'plantuml-view-exported-file) - keymap) - "Keymap for plantuml-mode.") - -(defcustom plantuml-java-command "java" - "The java command used to execute PlantUML." - :type 'string - :group 'plantuml) - -(defcustom plantuml-java-args (list "-Djava.awt.headless=true" "-jar" "--illegal-access=deny") - "The parameters passed to `plantuml-java-command' when executing PlantUML." - :type '(repeat string) - :group 'plantuml) - -(defcustom plantuml-jar-args (list "-charset" "UTF-8" ) - "The parameters passed to `plantuml.jar', when executing PlantUML." - :type '(repeat string) - :group 'plantuml) - -(defcustom plantuml-server-url "https://www.plantuml.com/plantuml" - "The base URL of the PlantUML server." - :type 'string - :group 'plantuml) - -(defcustom plantuml-executable-args (list "-headless") - "The parameters passed to plantuml executable when executing PlantUML." - :type '(repeat string) - :group 'plantuml) - -(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 - :options '(jar server executable)) - -(defcustom plantuml-suppress-deprecation-warning t - "To silence the deprecation warning when `puml-mode' is found upon loading." - :type 'boolean - :group 'plantuml) - -(defcustom plantuml-indent-level tab-width - "Indentation level of PlantUML lines" - :type 'number - :group 'plantuml) - -(defcustom plantuml-confirm-overwrite-on-export t - "Control whether file exporting is allowed to silently overwrite files." - :type 'boolean - :group 'plantuml) - -(defun plantuml-jar-render-command (&rest arguments) - "Create a command line to execute PlantUML with arguments (as ARGUMENTS)." - (let* ((cmd-list (append plantuml-java-args (list (expand-file-name plantuml-jar-path)) plantuml-jar-args arguments)) - (cmd (mapconcat 'identity cmd-list "|"))) - (plantuml-debug (format "Command is [%s]" cmd)) - cmd-list)) - -;;; syntax table -(defvar plantuml-mode-syntax-table - (let ((synTable (make-syntax-table))) - (modify-syntax-entry ?\/ ". 14c" synTable) - (modify-syntax-entry ?' "< 23" synTable) - (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.") - -;; PlantUML execution mode -(defvar-local plantuml-exec-mode nil - "The Plantuml execution mode override. See `plantuml-default-exec-mode' for acceptable values.") - -(defun plantuml-set-exec-mode (mode) - "Set the execution mode MODE for PlantUML." - (interactive (let* ((completion-ignore-case t) - (supported-modes '("jar" "server" "executable"))) - (list (completing-read (format "Exec mode [%s]: " plantuml-exec-mode) - supported-modes - nil - t - nil - nil - plantuml-exec-mode)))) - (if (member mode '("jar" "server" "executable")) - (setq plantuml-exec-mode (intern mode)) - (error (concat "Unsupported mode:" mode)))) - -(defun plantuml-get-exec-mode () - "Retrieves the currently active PlantUML exec mode." - (or plantuml-exec-mode - plantuml-default-exec-mode)) - -(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-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))) - (car) - (xml-node-children) - (car)))) - (message (concat "Downloading PlantUML v" version " into " plantuml-jar-path)) - (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.")) - (message "Aborted."))) - -(defun plantuml-jar-java-version () - "Inspects the Java runtime version of the configured Java command in `plantuml-java-command'." - (save-excursion - (save-match-data - (with-temp-buffer - (call-process plantuml-java-command nil t nil "-XshowSettings:properties" "-version") - (re-search-backward "java.version = \\(1.\\)?\\([[:digit:]]+\\)") - (string-to-number (match-string 2)))))) - -(defun plantuml-jar-get-language (buf) - "Retrieve the language specification from the PlantUML JAR file and paste it into BUF." - (unless (or (eq system-type 'cygwin) (file-exists-p plantuml-jar-path)) - (error "Could not find plantuml.jar at %s" plantuml-jar-path)) - (with-current-buffer buf - (let ((cmd-args (append (list plantuml-java-command nil t nil) - (plantuml-jar-render-command "-language")))) - (apply 'call-process cmd-args) - (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)))) - -(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))))) - -(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) - ('server #'plantuml-server-get-language) - ('executable #'plantuml-executable-get-language)))) - (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)) - (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)) - "txt" - (cond ((image-type-available-p 'svg) "svg") - ((image-type-available-p 'png) "png") - (t "txt"))) - "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")) - '("txt")))) - (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 non-nil if the diagram output format is an image, false if it's text based." - (not (equal "txt" plantuml-output-type))) - -(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)))) - -(defun plantuml-jar-start-process (buf) - "Run PlantUML as an Emacs process and puts the output into the given buffer (as BUF)." - (let ((java-args (if (<= 8 (plantuml-jar-java-version)) - (remove "--illegal-access=deny" plantuml-java-args) - plantuml-java-args))) - (apply #'start-process - "PLANTUML" buf plantuml-java-command - `(,@java-args - ,(expand-file-name plantuml-jar-path) - ,(plantuml-jar-output-type-opt plantuml-output-type) - ,@plantuml-jar-args - "-p")))) - -(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"))) - -(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))))) - -(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) t))) - (concat plantuml-server-url "/" plantuml-output-type "/-base64-" encoded-string))) - -(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: -- 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* ((url-request-location (plantuml-server-encode-url string))) - (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))))))) - -(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))))) - -(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) - ('server #'plantuml-server-preview-string) - ('executable #'plantuml-executable-preview-string)))) - (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." - (let ((b (get-buffer plantuml-preview-buffer))) - (when b - (kill-buffer b))) - - (let* ((imagep (and (display-images-p) - (plantuml-is-image-output-p))) - (buf (get-buffer-create plantuml-preview-buffer)) - (coding-system-for-read (and imagep 'binary)) - (coding-system-for-write (and imagep 'binary))) - (plantuml-exec-mode-preview-string prefix (plantuml-get-exec-mode) string buf))) - -(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 begin end) - "Preview diagram from the PlantUML sources in from BEGIN to END. -Uses the current region when called interactively. -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\nr") - (plantuml-preview-string prefix (concat "@startuml\n" - (buffer-substring-no-properties - begin end) - "\n@enduml"))) - -(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))) - -(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 (region-beginning) (region-end)) - (plantuml-preview-buffer prefix))) - - -;; Exporting to buffers - -(cl-defgeneric plantuml-export-string-to-buffer (exec-mode string buffer) - "Export STRING to plantuml diagram using the given EXEC-MODE. -Use BUFFER to write the diagram to, clearing it first. This -buffer can be written to a file or be displayed directly." - (ignore string buffer) - (user-error "Exporting with exec-mode %s is not supported (yet)" exec-mode)) - -(cl-defmethod plantuml-export-string-to-buffer ((exec-mode (eql jar)) string buffer) - "Method to export plantuml diagrams in STRING to BUFFER using jar EXEC-MODE." - (ignore exec-mode) - (unless (bufferp buffer) - (user-error "Given argument BUFFER is not a buffer: %s" buffer)) - - (let ((java-args (if (<= 8 (plantuml-jar-java-version)) - (remove "--illegal-access=deny" plantuml-java-args) - plantuml-java-args))) - - (with-current-buffer buffer - (erase-buffer)) - - (let ((return-code (apply #'call-process-region - string nil - plantuml-java-command - nil buffer nil - `(,@java-args - ,(expand-file-name plantuml-jar-path) - ,(plantuml-jar-output-type-opt plantuml-output-type) - ,@plantuml-jar-args - "-p")))) - - ;; If an error occured, return the return-code, and nil otherwise to - ;; signal everything's fine. - (unless (zerop return-code) - return-code)))) - -;; TODO add support for exec-modes `server' and `executable' - -(defun plantuml--export-file-name () - "Return export file name based on the file name associated with the current buffer." - (let ((original-file-name (buffer-file-name (current-buffer)))) - - (unless original-file-name - (error "Current buffer is not associated with any file, cannot determine output file name")) - - (concat (file-name-sans-extension original-file-name) - "." - plantuml-output-type))) - -(defun plantuml-export (&optional export-file-name) - "Export a plantuml diagram to a file. -Exports the whole buffer unless a region is active, in which case -only the region will be exported. Unless EXPORT-FILE-NAME is -explicitly given, the file name of the exported file will be -determined based on the file name of the current plantuml-file -and `plantuml-output-type'" - (interactive) - (let ((diagram-string (if mark-active - (buffer-substring-no-properties - (region-beginning) (region-end)) - (buffer-string))) - errors-during-export) - - (unless export-file-name - (setq export-file-name (plantuml--export-file-name))) - - (unless (or (not (file-exists-p export-file-name)) - (not plantuml-confirm-overwrite-on-export) - (yes-or-no-p (message "File %s already exists, overwrite?" export-file-name))) - (user-error "File %s already exists, will not overwrite" export-file-name)) - - (message "Exporting to %s ..." export-file-name) - - ;; If there's already a buffer visiting that file, use that for the export; - ;; otherwise create a new buffer. - (let ((target-buffer (or (find-buffer-visiting export-file-name) - (generate-new-buffer " *temp*")))) - ;; When the buffer is in image-mode, erasing it's contents is not - ;; possible, even when disabling `read-only-mode' explicitly. Let's just - ;; switch to `fundamental-mode' to keep things simple. - (with-current-buffer target-buffer - (fundamental-mode)) - ;; We do not use `with-temp-buffer' here, as we want to stay in the - ;; current buffer containing the plantuml diagram. The reason for this is - ;; that `planumlt-output-type' is local to the current buffer, and - ;; switching the buffer using `with-temp-buffer' may change its value and - ;; thus may give inconsistent results (e.g., files with ending "svg" that - ;; still contain a png). - (setq errors-during-export - (plantuml-export-string-to-buffer (plantuml-get-exec-mode) - diagram-string - target-buffer)) - ;; We export the result even in case of errors, as plantuml writes its - ;; error messages to the target output. Furthermore, exporting is always - ;; done without any conversion, as plantuml already outputs the desired - ;; format bit by bit. - (with-current-buffer target-buffer - (let ((coding-system-for-write 'binary)) - ;; When exporting, we do not want to display the image in Emacs - ;; itself; as rendering the image in Emacs may take a significant - ;; amount of time, we try to inhibit the automatic image display - ;; by binding `image-mode' to `image-mode-as-text' - (let ((auto-mode-alist nil) - (magic-fallback-mode-alist nil)) - (write-file export-file-name))))) - - (if errors-during-export - (message "Exporting to %s ... failed (see file for details)" export-file-name) - (message "Exporting to %s ... done" export-file-name)))) - - - -;; View exported files - -(defun plantuml-view-exported-file () - "Open file exported from current plantuml-buffer in external application. -When an exported file does not exist yet, ask whether it should -be generated first." - (interactive) - - (let ((export-file-name (plantuml--export-file-name))) - - (when (not (file-exists-p export-file-name)) - (if (yes-or-no-p (message "Export file %s does not exist yet, export current buffer?" - export-file-name)) - (plantuml-export export-file-name) - (user-error "Export file name %s does not exist yet and export has been denied, aborting" - export-file-name))) - - (cl-case system-type - ((windows-nt) (w32-shell-execute "open" export-file-name)) - ((cygwin) (start-process "" nil "cygstart" export-file-name)) - (otherwise (start-process "" nil "xdg-open" export-file-name))))) - - -;; - -;; Below are the regexp's for indentation. -;; Notes: -;; - there is some control on what it is indented by overriding some of below -;; X-start and X-end regexp before plantuml-mode is loaded. E.g., to disable -;; indentation on activate, you might define in your .emacs something like -;; (setq plantuml-indent-regexp-activate-start -;; "NEVER MATCH THIS EXPRESSION"); define _before_ load plantuml-mode! -;; (setq plantuml-indent-regexp-activate-end -;; "NEVER MATCH THIS EXPRESSION"); define _before_ load plantuml-mode! -;; - due to the nature of using (context-insensitive) regexp, indentation have -;; following limitations -;; - commands commented out by /' ... '/ will _not_ be ignored -;; and potentially lead to miss-indentation -;; - you can though somewhat correct mis-indentation by adding in '-comment lines -;; PLANTUML_MODE_INDENT_INCREASE and/or PLANTUML_MODE_INDENT_DECREASE -;; to increase and/or decrease the level of indentation -;; (Note: the line with the comment should not contain any text matching other indent -;; regexp or this user-control instruction will be ignored; also at most will count -;; per line ...) - -(defvar plantuml-indent-regexp-block-start "^.*{[[:blank:]]*$" - "Indentation regex for all plantuml elements that might define a {} block. -Plantuml elements like skinparam, rectangle, sprite, package, etc. -The opening { has to be the last visible character in the line (whitespace -might follow).") -(defvar plantuml-indent-regexp-note-start "^[[:blank:]]*\\(floating[[:blank:]]+\\)?[hr]?note[[:blank:]]+\\(right\\|left\\|top\\|bottom\\|over\\)[^:]*?$" - "simplyfied regex; note syntax is especially inconsistent across diagrams") -(defvar plantuml-indent-regexp-group-start "^[[:blank:]]*\\(alt\\|else\\|opt\\|loop\\|par\\|break\\|critical\\|group\\)\\(?:[[:blank:]]+.+\\|$\\)" - "Indentation regex for plantuml group elements that are defined for sequence diagrams. -Two variants for groups: keyword is either followed by whitespace and some text -or it is followed by line end.") -(defvar plantuml-indent-regexp-activate-start "^[[:blank:]]*\\(activate[[:blank:]]+.+\\|[^\\+]+\\+\\+.*\\)$") -(defvar plantuml-indent-regexp-box-start "^[[:blank:]]*box[[:blank:]]+.+$") -(defvar plantuml-indent-regexp-ref-start "^[[:blank:]]*ref[[:blank:]]+over[[:blank:]]+[^:]+?$") -(defvar plantuml-indent-regexp-title-start "^[[:blank:]]*title[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-header-start "^[[:blank:]]*\\(?:\\(?:center\\|left\\|right\\)[[:blank:]]+header\\|header\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-footer-start "^[[:blank:]]*\\(?:\\(?:center\\|left\\|right\\)[[:blank:]]+footer\\|footer\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-legend-start "^[[:blank:]]*\\(?:legend\\|legend[[:blank:]]+\\(?:bottom\\|top\\)\\|legend[[:blank:]]+\\(?:center\\|left\\|right\\)\\|legend[[:blank:]]+\\(?:bottom\\|top\\)[[:blank:]]+\\(?:center\\|left\\|right\\)\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-oldif-start "^.*if[[:blank:]]+\".*\"[[:blank:]]+then[[:blank:]]*\\('.*\\)?$" - "used in current activity diagram, sometimes already mentioned as deprecated") -(defvar plantuml-indent-regexp-newif-start "^[[:blank:]]*\\(?:else\\)?if[[:blank:]]+(.*)[[:blank:]]+then[[:blank:]]*.*$") -(defvar plantuml-indent-regexp-loop-start "^[[:blank:]]*\\(?:repeat[[:blank:]]*\\|while[[:blank:]]+(.*).*\\)$") -(defvar plantuml-indent-regexp-fork-start "^[[:blank:]]*\\(?:fork\\|split\\)\\(?:[[:blank:]]+again\\)?[[:blank:]]*$") -(defvar plantuml-indent-regexp-case-start "^[[:blank:]]*\\(?:switch\\|case\\)[[:blank:]]*(.*)[[:blank:]]*$") -(defvar plantuml-indent-regexp-macro-start "^[[:blank:]]*!definelong.*$") -(defvar plantuml-indent-regexp-user-control-start "^.*'.*[[:blank:]]*PLANTUML_MODE_INDENT_INCREASE[[:blank:]]*.*$") -(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-newif-start - plantuml-indent-regexp-loop-start - plantuml-indent-regexp-fork-start - plantuml-indent-regexp-case-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 - plantuml-indent-regexp-user-control-start)) -(defvar plantuml-indent-regexp-block-end "^[[:blank:]]*\\(?:}\\|endif\\|else[[:blank:]]*.*\\|end\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-note-end "^[[:blank:]]*\\(end[[:blank:]]+note\\|end[rh]note\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-group-end "^[[:blank:]]*end[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-activate-end "^[[:blank:]]*\\(?:deactivate[[:blank:]]+.+\\|return.*\\)$") -(defvar plantuml-indent-regexp-box-end "^[[:blank:]]*end[[:blank:]]+box[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-ref-end "^[[:blank:]]*end[[:blank:]]+ref[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-title-end "^[[:blank:]]*end[[:blank:]]+title[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-header-end "^[[:blank:]]*endheader[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-footer-end "^[[:blank:]]*endfooter[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-legend-end "^[[:blank:]]*endlegend[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-oldif-end "^[[:blank:]]*\\(endif\\|else\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-newif-end "^[[:blank:]]*\\(endif\\|elseif\\|else\\)[[:blank:]]*.*$") -(defvar plantuml-indent-regexp-loop-end "^[[:blank:]]*\\(repeat[[:blank:]]*while\\|endwhile\\)[[:blank:]]*.*$") -(defvar plantuml-indent-regexp-fork-end "^[[:blank:]]*\\(\\(fork\\|split\\)[[:blank:]]+again\\|end[[:blank:]]+\\(fork\\|split\\)\\)[[:blank:]]*$") -(defvar plantuml-indent-regexp-case-end "^[[:blank:]]*\\(case[[:blank:]]*(.*)\\|endswitch\\)[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-macro-end "^[[:blank:]]*!enddefinelong[[:blank:]]*\\('.*\\)?$") -(defvar plantuml-indent-regexp-user-control-end "^.*'.*[[:blank:]]*PLANTUML_MODE_INDENT_DECREASE[[:blank:]]*.*$") -(defvar plantuml-indent-regexp-end (list plantuml-indent-regexp-block-end - plantuml-indent-regexp-group-end - plantuml-indent-regexp-activate-end - plantuml-indent-regexp-box-end - plantuml-indent-regexp-ref-end - plantuml-indent-regexp-legend-end - plantuml-indent-regexp-note-end - plantuml-indent-regexp-newif-end - plantuml-indent-regexp-loop-end - plantuml-indent-regexp-fork-end - plantuml-indent-regexp-case-end - plantuml-indent-regexp-title-end - plantuml-indent-regexp-header-end - plantuml-indent-regexp-footer-end - plantuml-indent-regexp-macro-end - plantuml-indent-regexp-oldif-end - plantuml-indent-regexp-user-control-end)) - -(defun plantuml-init-once (&optional mode force) - "Initialize regular expressions for font locking. -Use exec mode MODE to load the language details or by first -querying `plantuml-get-exec-mode'. Ensure that initialization is -only performed when not already done. To force reinitalization, -provide a non-nil value for FORCE." - (let ((mode (or mode (plantuml-get-exec-mode)))) - (when (or (not plantuml-kwdList) - force) - (plantuml-init mode) - (let ((plantuml-types-regexp (concat "^[[:blank:]]*\\(" - (regexp-opt plantuml-types 'words) - "\\|\\<\\(note[[:blank:]]+over\\|note[[:blank:]]+\\(left\\|right\\|bottom\\|top\\)[[:blank:]]+\\(of\\)?\\)\\>\\|\\<\\(\\(left\\|center\\|right\\)[[:blank:]]+\\(header\\|footer\\)\\)\\>\\)")) - (plantuml-keywords-regexp (concat "^[[:blank:]]*" - (regexp-opt plantuml-keywords 'words) - "\\|\\(<\\|<|\\|\\*\\|o\\)\\(\\.+\\|-+\\)\\|\\(\\.+\\|-+\\)\\(>\\||>\\|\\*\\|o\\)\\|\\.\\{2,\\}\\|-\\{2,\\}")) - (plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words)) - (plantuml-preprocessors-regexp (concat "^[[:blank:]]*" - (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))))) - -(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"))))) - - -;; indentation - - -(defun plantuml-current-block-depth () - "Trace the current block indentation level by recursively looking back line by line." - (save-excursion - (let ((relative-depth 0)) - ;; current line - (beginning-of-line) - (if (-any? '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) - (if (-any? 'looking-at plantuml-indent-regexp-end) - (setq relative-depth (1- relative-depth))) - (if (-any? 'looking-at plantuml-indent-regexp-start) - (setq relative-depth (1+ relative-depth)))) - - (if (<= relative-depth 0) - 0 - relative-depth)))) - -(defun plantuml-indent-line () - "Indent the current line to its desired indentation level. -Restore point to same position in text of the line as before indentation." - (interactive) - ;; store position of point in line measured from end of line - (let ((original-position-eol (- (line-end-position) (point)))) - (save-excursion - (beginning-of-line) - (indent-line-to (* plantuml-indent-level (plantuml-current-block-depth)))) - - ;; restore position in text of line, but not before the beginning of the - ;; current line - (goto-char (max (line-beginning-position) - (- (line-end-position) original-position-eol))))) - - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.\\(plantuml\\|pum\\|plu\\)\\'" . plantuml-mode)) - -;;;###autoload -(define-derived-mode plantuml-mode prog-mode "plantuml" - "Major mode for plantuml. - -Shortcuts Command Name -\\[plantuml-complete-symbol] `plantuml-complete-symbol'" - (plantuml-init-once) - (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) - (set (make-local-variable 'indent-line-function) 'plantuml-indent-line) - (setq font-lock-defaults '((plantuml-font-lock-keywords) nil t))) - -(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 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