diff --git a/plantuml-mode.el b/plantuml-mode.el index e40ba0d..397ecc5 100644 --- a/plantuml-mode.el +++ b/plantuml-mode.el @@ -568,28 +568,35 @@ Uses prefix (as PREFIX) to choose where to display it: ;; (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 ...) + ;; Quirks of elisp REGEXes: + ;; \s is NOT translated to mean any whitespace character as in other + ;; regex flavours, but instead translates to ' ' (= one empty + ;; space). This does not match e.g. tabs. Use [[:blank:]] instead, to + ;; match 'classic' whitespace chars. [[:space:]] seems an option, but + ;; also matches linebreaks, etc. which could cause problems, as more + ;; than one line might be matched. (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+.+\\|$\\)" + (defvar plantuml-indent-regexp-note-start "^[[:blank:]]*\\(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 "^[[:blank:]]*\\(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-activate-start "^[[:blank:]]*activate\s+.+$") + (defvar plantuml-indent-regexp-box-start "^[[:blank:]]*box\s+.+$") + (defvar plantuml-indent-regexp-ref-start "^[[:blank:]]*ref\s+over\s+[^:]+?$") + (defvar plantuml-indent-regexp-title-start "^[[:blank:]]*title\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-header-start "^[[:blank:]]*\\(?:\\(?:center\\|left\\|right\\)\s+header\\|header\\)\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-footer-start "^[[:blank:]]*\\(?:\\(?:center\\|left\\|right\\)\s+footer\\|footer\\)\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-legend-start "^[[:blank:]]*\\(?: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-newif-start "^[[:blank:]]*\\(?:else\\)?if\s+(.*)\s+then\s*.*$") + (defvar plantuml-indent-regexp-loop-start "^[[:blank:]]*\\(?:repeat\s*\\|while\s+(.*).*\\)$") + (defvar plantuml-indent-regexp-fork-start "^[[:blank:]]*\\(?:fork\\|split\\)\\(?:\s+again\\)?\s*$") + (defvar plantuml-indent-regexp-macro-start "^[[:blank:]]*!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 @@ -607,21 +614,21 @@ or it is followed by line end.") 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-block-end "^[[:blank:]]*\\(?:}\\|endif\\|else\s*.*\\|end\\)\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-note-end "^[[:blank:]]*\\(end\s+note\\|end[rh]note\\)\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-group-end "^[[:blank:]]*end\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-activate-end "^[[:blank:]]*deactivate\s+.+$") + (defvar plantuml-indent-regexp-box-end "^[[:blank:]]*end\s+box\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-ref-end "^[[:blank:]]*end\s+ref\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-title-end "^[[:blank:]]*end\s+title\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-header-end "^[[:blank:]]*endheader\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-footer-end "^[[:blank:]]*endfooter\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-legend-end "^[[:blank:]]*endlegend\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-oldif-end "^[[:blank:]]*\\(endif\\|else\\)\s*\\('.*\\)?$") + (defvar plantuml-indent-regexp-newif-end "^[[:blank:]]*\\(endif\\|elseif\\|else\\)\s*.*$") + (defvar plantuml-indent-regexp-loop-end "^[[:blank:]]*\\(repeat\s*while\\|endwhile\\)\s*.*$") + (defvar plantuml-indent-regexp-fork-end "^[[:blank:]]*\\(\\(fork\\|split\\)\s+again\\|end\s+\\(fork\\|split\\)\\)\s*$") + (defvar plantuml-indent-regexp-macro-end "^[[:blank:]]*!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 diff --git a/test/plantuml-indentation-with-tabs-test.el b/test/plantuml-indentation-with-tabs-test.el new file mode 100644 index 0000000..a3ccbc4 --- /dev/null +++ b/test/plantuml-indentation-with-tabs-test.el @@ -0,0 +1,111 @@ +;;; plantuml-indentation-with-tabs-test.el --- PlantUML Mode indentation tests -*- lexical-binding: t; -*- + +;; Author: René Schmelzer, Tobias Marczewski (mtoboid) +;; Maintainer: Carlo Sciolla (skuro) +;; URL: https://github.com/skuro/plantuml-mode + +;;; Commentary: + +;; Test indentation for class diagrams, specifically using tabs. + +;;; Code: + +(ert-deftest plantuml-test-indentation/tabs/nested-modules () + "Test correct indentation of plantuml class diagram elements. +These code examples are taken from www.plantuml.com" + (plantuml-test-indent-block-with-tabs + + " +@startuml + +'some comment +package org.example.module1 { +interface A { +doStuff(): String +getList(): List +} + +class B { +-name: String ++getName(): String +} +} + +package org.example.module2 { +class C { +-count: int ++getCount(): int +} +} + +A <|.. B +A <|.. C + +@enduml +" + " +@startuml + +'some comment +package org.example.module1 { + interface A { + doStuff(): String + getList(): List + } + + class B { + -name: String + +getName(): String + } +} + +package org.example.module2 { + class C { + -count: int + +getCount(): int + } +} + +A <|.. B +A <|.. C + +@enduml +")) + + +(ert-deftest plantuml-test-block-indentation/tabs/package-empty () + "Test correct indentation of an empty package block." + (plantuml-test-indent-block-with-tabs + " +package APackage () +interface Inter +" + " +package APackage () +interface Inter +")) + + +(ert-deftest platuml-test-block-indentation/tabs/package-interface-nested () + "Test correct indentation of two nested blocks, a package and an interface +Note: package is used in deployment and object diagrams as well, see there for more tests." + (plantuml-test-indent-block-with-tabs + " +package foo { +interface Bar { +baz +} +} +" + " +package foo { + interface Bar { + baz + } +} +")) + + +(provide 'plantuml-indentation-with-tabs-test) + +;;; plantuml-indentation-with-tabs-test.el ends here diff --git a/test/test-helper.el b/test/test-helper.el index feb3950..6642066 100644 --- a/test/test-helper.el +++ b/test/test-helper.el @@ -66,6 +66,38 @@ Finally, the indented text in the buffer will be compared with AFTER." (indent-region (point-min) (point-max)) (should (equal (buffer-string) after)))) +;; FIXME +;; This function is just a copy of the above plantuml-test-indent-block with +;; some minor changes to use indentation with tabs. Perhaps merge the two +;; functions? +(defun plantuml-test-indent-block-with-tabs (before after) + "Helper for the block indentation tests with tabs. + +BEFORE is the text block to be inserted into a temporary buffer. +AFTER is the expected text block after indentation. + +The temporary buffer will be put into `plantuml-mode'. The whole buffer +will be indented with one tab for each level of indentation. + +Finally, the indented text in the buffer will be compared with AFTER." + + ;; ensure that plantuml-indent-level is the default value 8 + (let ((indent-tabs-mode t) + (plantuml-indent-level 8) + ;; fix the JAR location prior to mode initialization + ;; for some reason, plantuml-mode disregards the setq-local + (plantuml-jar-path plantuml-test-jar-path)) + + (with-temp-buffer + (plantuml-init-once 'jar) + + (insert before) + (goto-char (point-min)) + (plantuml-mode) + + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) after))))) + ;; enable code coverage (when (require 'undercover nil t) (undercover "plantuml-mode.el"))