From d7721971f0695f0402f3f47bf8d2ce9f1fc7c119 Mon Sep 17 00:00:00 2001 From: Daniel Borchmann Date: Sat, 3 Jul 2021 09:54:25 +0200 Subject: [PATCH] Allow reinitialization of font lock regexps In `plantuml-mode`, initialization of the font locking regular expressions is done by asking the current version of plantuml for its supported keywords, builtins, etc. Every so often, this might fail for any reason, leaving the current Emacs session with invalid font lock information for plantuml. Redoing the initialization process is tedious, as the corresponding function `plantuml-init-once` does not allow for that to be done easily (one would have to undefine a lot of global variables to do so). This commit introduces a force parameter for `plantuml-init-once` to conduct reinitialization of all font lock information. It also moves regular expressions meant for indentation out of `plantuml-init-once`, as those are initialized by literal values that do not depend on any runtime information. --- plantuml-mode.el | 243 ++++++++++++++++++++++++----------------------- 1 file changed, 126 insertions(+), 117 deletions(-) diff --git a/plantuml-mode.el b/plantuml-mode.el index e40ba0d..4b6feeb 100644 --- a/plantuml-mode.el +++ b/plantuml-mode.el @@ -539,131 +539,140 @@ Uses prefix (as PREFIX) to choose where to display it: (plantuml-preview-region prefix (region-beginning) (region-end)) (plantuml-preview-buffer prefix))) -(defun plantuml-init-once (&optional mode) - "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)))) - (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))) +;; 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 ...) - ;; 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 "^.*{\s*$" - "Indentation regex for all plantuml elements that might define a {} block. +(defvar plantuml-indent-regexp-block-start "^.*{\s*$" + "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 "^\s*\\(floating\s+\\)?[hr]?note\s+\\(right\\|left\\|top\\|bottom\\|over\\)[^:]*?$" "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. +(defvar plantuml-indent-regexp-note-start "^\s*\\(floating\s+\\)?[hr]?note\s+\\(right\\|left\\|top\\|bottom\\|over\\)[^:]*?$" + "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. 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 "^\s*activate\s+.+$") - (defvar plantuml-indent-regexp-box-start "^\s*box\s+.+$") - (defvar plantuml-indent-regexp-ref-start "^\s*ref\s+over\s+[^:]+?$") - (defvar plantuml-indent-regexp-title-start "^\s*title\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-header-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+header\\|header\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-footer-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+footer\\|footer\\)\s*\\('.*\\)?$") - (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\\)\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-oldif-start "^.*if\s+\".*\"\s+then\s*\\('.*\\)?$" "used in current activity diagram, sometimes already mentioned as deprecated") - (defvar plantuml-indent-regexp-newif-start "^\s*\\(?:else\\)?if\s+(.*)\s+then\s*.*$") - (defvar plantuml-indent-regexp-loop-start "^\s*\\(?:repeat\s*\\|while\s+(.*).*\\)$") - (defvar plantuml-indent-regexp-fork-start "^\s*\\(?:fork\\|split\\)\\(?:\s+again\\)?\s*$") - (defvar plantuml-indent-regexp-macro-start "^\s*!definelong.*$") - (defvar plantuml-indent-regexp-user-control-start "^.*'.*\s*PLANTUML_MODE_INDENT_INCREASE\s*.*$") - (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-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 "^\s*\\(?:}\\|endif\\|else\s*.*\\|end\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-note-end "^\s*\\(end\s+note\\|end[rh]note\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-group-end "^\s*end\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-activate-end "^\s*deactivate\s+.+$") - (defvar plantuml-indent-regexp-box-end "^\s*end\s+box\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-ref-end "^\s*end\s+ref\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-title-end "^\s*end\s+title\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-header-end "^\s*endheader\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-footer-end "^\s*endfooter\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-legend-end "^\s*endlegend\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-oldif-end "^\s*\\(endif\\|else\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-newif-end "^\s*\\(endif\\|elseif\\|else\\)\s*.*$") - (defvar plantuml-indent-regexp-loop-end "^\s*\\(repeat\s*while\\|endwhile\\)\s*.*$") - (defvar plantuml-indent-regexp-fork-end "^\s*\\(\\(fork\\|split\\)\s+again\\|end\s+\\(fork\\|split\\)\\)\s*$") - (defvar plantuml-indent-regexp-macro-end "^\s*!enddefinelong\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-user-control-end "^.*'.*\s*PLANTUML_MODE_INDENT_DECREASE\s*.*$") - (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-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)) - (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 - )) +(defvar plantuml-indent-regexp-activate-start "^\s*activate\s+.+$") +(defvar plantuml-indent-regexp-box-start "^\s*box\s+.+$") +(defvar plantuml-indent-regexp-ref-start "^\s*ref\s+over\s+[^:]+?$") +(defvar plantuml-indent-regexp-title-start "^\s*title\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-header-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+header\\|header\\)\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-footer-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+footer\\|footer\\)\s*\\('.*\\)?$") +(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\\)\\)\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-oldif-start "^.*if\s+\".*\"\s+then\s*\\('.*\\)?$" + "used in current activity diagram, sometimes already mentioned as deprecated") +(defvar plantuml-indent-regexp-newif-start "^\s*\\(?:else\\)?if\s+(.*)\s+then\s*.*$") +(defvar plantuml-indent-regexp-loop-start "^\s*\\(?:repeat\s*\\|while\s+(.*).*\\)$") +(defvar plantuml-indent-regexp-fork-start "^\s*\\(?:fork\\|split\\)\\(?:\s+again\\)?\s*$") +(defvar plantuml-indent-regexp-macro-start "^\s*!definelong.*$") +(defvar plantuml-indent-regexp-user-control-start "^.*'.*\s*PLANTUML_MODE_INDENT_INCREASE\s*.*$") +(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-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 "^\s*\\(?:}\\|endif\\|else\s*.*\\|end\\)\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-note-end "^\s*\\(end\s+note\\|end[rh]note\\)\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-group-end "^\s*end\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-activate-end "^\s*deactivate\s+.+$") +(defvar plantuml-indent-regexp-box-end "^\s*end\s+box\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-ref-end "^\s*end\s+ref\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-title-end "^\s*end\s+title\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-header-end "^\s*endheader\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-footer-end "^\s*endfooter\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-legend-end "^\s*endlegend\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-oldif-end "^\s*\\(endif\\|else\\)\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-newif-end "^\s*\\(endif\\|elseif\\|else\\)\s*.*$") +(defvar plantuml-indent-regexp-loop-end "^\s*\\(repeat\s*while\\|endwhile\\)\s*.*$") +(defvar plantuml-indent-regexp-fork-end "^\s*\\(\\(fork\\|split\\)\s+again\\|end\s+\\(fork\\|split\\)\\)\s*$") +(defvar plantuml-indent-regexp-macro-end "^\s*!enddefinelong\s*\\('.*\\)?$") +(defvar plantuml-indent-regexp-user-control-end "^.*'.*\s*PLANTUML_MODE_INDENT_DECREASE\s*.*$") +(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-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)) - (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-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 "^\\s *\\(" + (regexp-opt plantuml-types 'words) + "\\|\\<\\(note\\s +over\\|note\\s +\\(left\\|right\\|bottom\\|top\\)\\s +\\(of\\)?\\)\\>\\|\\<\\(\\(left\\|center\\|right\\)\\s +\\(header\\|footer\\)\\)\\>\\)")) + (plantuml-keywords-regexp (concat "^\\s *" + (regexp-opt plantuml-keywords 'words) + "\\|\\(<\\|<|\\|\\*\\|o\\)\\(\\.+\\|-+\\)\\|\\(\\.+\\|-+\\)\\(>\\||>\\|\\*\\|o\\)\\|\\.\\{2,\\}\\|-\\{2,\\}")) + (plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words)) + (plantuml-preprocessors-regexp (concat "^\\s *" + (regexp-opt plantuml-preprocessors 'words)))) - ;; 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)))) + (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) + + (setq plantuml-types nil + plantuml-keywords nil + plantuml-builtins nil + plantuml-preprocessors nil))))) (defun plantuml-complete-symbol () "Perform keyword completion on word before cursor."