diff --git a/plantuml-mode.el b/plantuml-mode.el index 733f95c..4484810 100644 --- a/plantuml-mode.el +++ b/plantuml-mode.el @@ -340,8 +340,8 @@ Uses prefix (as PREFIX) to choose where to display it: (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-start "^[ \t]*\\(\\(?:.*\\)?\s*\\(?:[<>.*a-z-|]+\\)?\s*\\(?:\\[[a-zA-Z]+\\]\\)?\s+if\\|alt\\|else\\|note\s+over\\|note\sas\s.*\\|note\s+\\(\\(?:\\(?:buttom\\|left\\|right\\|top\\)\\)\\)\\(?:\s+of\\)?\\|\\(?:class\\|enum\\|package\\)\s+.*{\\)") - (defvar plantuml-indent-regexp-end "^[ \t]*\\(endif\\|else\\|end\\|end\s+note\\|.*}\\)") + (defvar plantuml-indent-regexp-start "^[ \t]*\\(\\(?:.*\\)?\s*\\(?:[<>.*a-z-|]+\\)?\s*\\(?:\\[[a-zA-Z]+\\]\\)?\s+if\s+.*\\|loop\s+.*\\|group\s+.*\\|par\s*$\\|opt\s+.*\\|alt\s+.*\\|else\\|note\s+over\\|note\sas\s.*\\|note\s+\\(\\(?:\\(?:buttom\\|left\\|right\\|top\\)\\)\\)\\(?:\s+of\\)?\\|\\(?:class\\|enum\\|package\\|database\\)\s+.*{\\|activate\s+.+\\)") + (defvar plantuml-indent-regexp-end "^[ \t]*\\(endif\\|else\\|end\\|end\s+note\\|.*}\\|deactivate\s+.+\\)") (setq plantuml-font-lock-keywords `( @@ -402,41 +402,39 @@ Uses prefix (as PREFIX) to choose where to display it: (defvar plantuml-indent-regexp-start) (defvar plantuml-indent-regexp-end) (save-excursion - (let ((relative-depth 0) - (bob-visited? nil)) + (let ((relative-depth 0)) + ;; current line (beginning-of-line) - (forward-line -1) - (while (and (>= relative-depth 0) - (not bob-visited?)) - (if (bobp) - (setq bob-visited? t)) + (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) (if (looking-at plantuml-indent-regexp-end) (setq relative-depth (1- relative-depth))) (if (looking-at plantuml-indent-regexp-start) - (setq relative-depth (1+ relative-depth))) - (forward-line -1)) + (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." + "Indent the current line to its desired indentation level. +Restore point to same position in text of the line as before indentation." (interactive) ;; forward declare the lazy initialized constants (defvar plantuml-indent-regexp-start) (defvar plantuml-indent-regexp-end) - (let ((original-indentation (current-indentation))) + ;; store position of point in line measured from end of line + (let ((original-position-eol (- (line-end-position) (point)))) (save-excursion (beginning-of-line) - (if (bobp) - (indent-line-to 0) - (let ((depth (plantuml-current-block-depth))) - (when (looking-at plantuml-indent-regexp-end) - (setq depth (max (1- depth) 0))) - (indent-line-to (* tab-width depth))))) - (forward-char (- (current-indentation) - original-indentation)))) + (indent-line-to (* tab-width (plantuml-current-block-depth)))) + + ;; restore position in text of line + (goto-char (- (line-end-position) original-position-eol)))) ;;;###autoload diff --git a/test/plantuml-indentation-test.el b/test/plantuml-indentation-test.el index c8bc723..8791040 100644 --- a/test/plantuml-indentation-test.el +++ b/test/plantuml-indentation-test.el @@ -1,4 +1,4 @@ -;;; plantuml-mode-indentation-test.el --- PlantUML Mode indentation tests -*- lexical-binding: t; -*- +;;; plantuml-indentation-test.el --- PlantUML Mode indentation tests -*- lexical-binding: t; -*- ;; Author: Raymond Huang (rymndhng) ;; Maintainer: Carlo Sciolla (skuro) @@ -10,202 +10,499 @@ ;;; Code: -(defun add-text-and-position-cursor (txt) - "Add TXT into the buffer, move cursor to the position of the marker | and delete the marker." +(defun plantuml-test-add-text-and-position-cursor (txt) + "Test helper for `plantuml-mode' tests. +Add TXT into the buffer, move cursor to the position of the marker +?| and delete the marker." (insert txt) (goto-char (point-min)) (search-forward "|") (delete-char -1)) -(defun assert-block-depth (expected txt) - "Assert the EXPECTED indentation level for the given TXT." +(defun plantuml-test-assert-block-depth (expected txt) + "Test helper for `plantuml-mode' tests. +Assert the EXPECTED indentation level for the given TXT." (with-temp-buffer - (add-text-and-position-cursor txt) + (plantuml-test-add-text-and-position-cursor txt) (let ((actual (plantuml-current-block-depth))) (should (equal expected actual))))) -(ert-deftest test-plantuml-current-block-depth () +(ert-deftest plantuml-test-current-block-depth_bob () + "Test `plantuml-current-block-depth' level 0 at beginning of buffer." (setq-local plantuml-jar-path plantuml-test-jar-path) (plantuml-init-once) - (assert-block-depth 0 " -A |-> B") + (plantuml-test-assert-block-depth 0 "| +activate p1 + activate p2 + foo + deactivate p2 +deactivate p1 +") + ) - (assert-block-depth 0 " -pac|kage Foo { -A -> B -}") +(ert-deftest plantuml-test-current-block-depth_0 () + "Test `plantuml-current-block-depth' level 0 at beginning of first line." + (setq-local plantuml-jar-path plantuml-test-jar-path) + (plantuml-init-once) - (assert-block-depth 1 " + (plantuml-test-assert-block-depth 0 " +|activate p1 + activate p2 + foo + deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_1 () + "Test `plantuml-current-block-depth' level 0 at middle of first line." + (setq-local plantuml-jar-path plantuml-test-jar-path) + (plantuml-init-once) + + (plantuml-test-assert-block-depth 0 " +acti|vate p1 + activate p2 + foo + deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_2 () + "Test `plantuml-current-block-depth' level 0 at end of first line" + (setq-local plantuml-jar-path plantuml-test-jar-path) + (plantuml-init-once) + + (plantuml-test-assert-block-depth 0 " +activate p1| + activate p2 + foo + deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_3 () + "Test `plantuml-current-block-depth' level 1 at beginning of 2nd line." + + (plantuml-test-assert-block-depth 1 " +activate p1 + |activate p2 + foo + deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_4 () + "Test `plantuml-current-block-depth' level 2 at beginning of 3rd line." + + (plantuml-test-assert-block-depth 2 " +activate p1 + activate p2 + |foo + deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_5 () + "Test `plantuml-current-block-depth' level 1 at beginning of 4th line." + + (plantuml-test-assert-block-depth 1 " +activate p1 + activate p2 + foo + |deactivate p2 +deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_6 () + "Test `plantuml-current-block-depth' level 0 at beginning of 5th line." + + (plantuml-test-assert-block-depth 0 " +activate p1 + activate p2 + foo + deactivate p2 +|deactivate p1 +") + ) + +(ert-deftest plantuml-test-current-block-depth_eob () + "Test `plantuml-current-block-depth' level 0 at end of buffer." + + (plantuml-test-assert-block-depth 0 " +activate p1 + activate p2 + foo + deactivate p2 +deactivate p1 +|") + ) + +(defun plantuml-test-indent-line (before after) + "The common code for the line indentation tests. + +BEFORE is the text to be inserted into a temporary buffer. +AFTER is the expected text after indentation. + +Both, BEFORE and AFTER need to specify point with char |. The +temporary buffer will be put into `plantuml-mode', the char | +representing point will be removed from text. The line with the +removed | will be indented (just this line!) with two spaces for each +level of indentation. + +Finally, +1) the indented line will be compared with the same line in AFTER +2) the position of point in the indented line will be compared with +the position of | in AFTER." + + (let* ( (expected-cursor-pos (1+ (s-index-of "|" after))) + (expected-state (delete ?| after)) + ) + (with-temp-buffer + ;; fix the JAR location prior to mode initialization + ;; for some reason, plantuml-mode disregards the setq-local + (setq-local plantuml-jar-path plantuml-test-jar-path) + (plantuml-init-once) + + (plantuml-test-add-text-and-position-cursor before) + (plantuml-mode) + + ;; use 2 spaces instead of one tab for indentation + (setq-local indent-tabs-mode nil) + (setq-local tab-width 2) + (indent-according-to-mode) + + (should (equal expected-state (buffer-string))) + (should (equal expected-cursor-pos (point)))))) + +(ert-deftest plantuml-test-line-indentation/empty-line-l0 () + "Test correct indentation of empty line - indentation level 0." + (plantuml-test-indent-line "|" "|")) + +(ert-deftest plantuml-test-line-indentation/bol-notindent-l0 () + "Test correct indentation of a not indented line with point at beginning of line - indentation level 0." + (plantuml-test-indent-line "|participant A" + "|participant A")) + +(ert-deftest plantuml-test-line-indentation/mol-notindent-l0 () + "Test correct indentation of a not indented line with point at middle of line - indentation level 0." + (plantuml-test-indent-line "parti|cipant" + "parti|cipant")) + +(ert-deftest plantuml-test-line-indentation/eol-notindent-l0 () + "Test correct indentation of a not indented line with point at end of line - indentation level 0." + (plantuml-test-indent-line "participant A|" + "participant A|")) + +(ert-deftest plantuml-test-line-indentation/bol-indented-l0 () + "Test correct indentation of an indented line with point at beginning of line - indentation level 0." + (plantuml-test-indent-line " |participant A" + "|participant A")) + +(ert-deftest plantuml-test-line-indentation/mol-indented-l0 () + "Test correct indentation of an indented line with point at middle of line - indentation level 0." + (plantuml-test-indent-line " parti|cipant" + "parti|cipant")) + +(ert-deftest plantuml-test-line-indentation/eol-indented-l0 () + "Test correct indentation of an indented line with point at end of line - indentation level 0." + (plantuml-test-indent-line " participant A|" + "participant A|")) + +(ert-deftest plantuml-test-line-indentation/empty-line-l1 () + "Test correct indentation of empty line - indentation level 1." + (plantuml-test-indent-line + "opt A +|" + "opt A + |")) + +(ert-deftest plantuml-test-line-indentation/bol-notindent-l1 () + "Test correct indentation of a not indented line with point at beginning of line - indentation level 1." + (plantuml-test-indent-line "opt A +|foofoo" + "opt A + |foofoo")) + +(ert-deftest plantuml-test-line-indentation/mol-notindent-l1 () + "Test correct indentation of a not indented line with point at middle of line - indentation level 1." + (plantuml-test-indent-line "opt A +foo|foo" + "opt A + foo|foo")) + +(ert-deftest plantuml-test-line-indentation/eol-notindent-l1 () + "Test correct indentation of a not indented line with point at end of line - indentation level 1." + (plantuml-test-indent-line "opt A +foofoo|" + "opt A + foofoo|")) + +(ert-deftest plantuml-test-line-indentation/bol-indented-l1 () + "Test correct indentation of an indented line with point at beginning of line - indentation level 1." + (plantuml-test-indent-line " opt A + |foofoo" + " opt A + |foofoo")) + +(ert-deftest plantuml-test-line-indentation/mol-indented-l1 () + "Test correct indentation of an indented line with point at middle of line - indentation level 1." + (plantuml-test-indent-line " opt A + foo|foo" + " opt A + foo|foo")) + +(ert-deftest plantuml-test-line-indentation/eol-indented-l1 () + "Test correct indentation of an indented line with point at end of line - indentation level 1." + (plantuml-test-indent-line " opt A + foofoo|" + " opt A + foofoo|")) + + +(defun plantuml-test-indent-block (before after) + "The common code for the block indentation tests. + +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 two spaces for each level of indentation. + +Finally, the indented text in the buffer will be compared with AFTER." + + (with-temp-buffer + ;; fix the JAR location prior to mode initialization + ;; for some reason, plantuml-mode disregards the setq-local + (setq-local plantuml-jar-path plantuml-test-jar-path) + (plantuml-init-once) + + (insert before) + (goto-char (point-min)) + (plantuml-mode) + ;; use 2 spaces instead of one tab for indentation + (setq-local indent-tabs-mode nil) + (setq-local tab-width 2) + + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) after)) + )) + +(ert-deftest plantuml-test-block-indentation/package-empty () + "Test correct indentation of an empty package block." + (plantuml-test-indent-block + " +package APackage () +" + " +package APackage () +")) + +(ert-deftest plantuml-test-block-indentation/package () + "Test correct indentation of a package block." + (plantuml-test-indent-block + " package APackage { -|A -> B -}") +A -> B +} +" + " +package APackage { + A -> B +} +")) +(ert-deftest plantuml-test-block-indentation/package-database-nested () + "Test correct indentation of two nested blocks, a package and a database. +Note: currently the inner database is not indented." + (plantuml-test-indent-block + " +package APackage { + database ADatabase { + A -> B + } +} +" + " +package APackage { + database ADatabase { + A -> B + } +} +")) - (assert-block-depth 1 " +(ert-deftest plantuml-test-block-indentation/alt-end () + "Test correct indentation of an alt-end block." + (plantuml-test-indent-block + " alt choice 1 -|A -> B +A -> B +end +" + " +alt choice 1 + A -> B +end +" )) + +(ert-deftest plantuml-test-block-indentation/alt-else-end () + "Test correct indentation of an alt-else-end block." + (plantuml-test-indent-block + " +alt choice 1 +A -> B +else +B -> C +end +" + " +alt choice 1 + A -> B +else + B -> C +end +" )) + +(ert-deftest plantuml-test-block-indentation/opt () + "Test correct indentation of an opt block." + (plantuml-test-indent-block + " +opt event triggered +A -> B +end +" + " +opt event triggered + A -> B +end +" )) + +(ert-deftest plantuml-test-block-indentation/par () + "Test correct indentation of a par block." + (plantuml-test-indent-block + " +par +A -> B +else +C -> B +end +" + " +par + A -> B +else + C -> B +end +" )) + + +(ert-deftest plantuml-test-block-indentation/alt-else-loop-group () + "Test correct indentation of combination of alt-else, loop and group. + +This is taken from the plantuml homepage." + (plantuml-test-indent-block + " +Alice -> Bob: Authentication Request + +alt successful case + +Bob -> Alice: Authentication Accepted + +else some kind of failure + +Bob -> Alice: Authentication Failure +group My own label +Alice -> Log : Log attack start +loop 1000 times +Alice -> Bob: DNS Attack +end +Alice -> Log : Log attack end +end + +else Another type of failure + +Bob -> Alice: Please repeat + +end +" + " +Alice -> Bob: Authentication Request + +alt successful case + + Bob -> Alice: Authentication Accepted + +else some kind of failure + + Bob -> Alice: Authentication Failure + group My own label + Alice -> Log : Log attack start + loop 1000 times + Alice -> Bob: DNS Attack + end + Alice -> Log : Log attack end + end + +else Another type of failure + + Bob -> Alice: Please repeat + end ")) -;; This is taken from https://github.com/clojure-emacs/clojure-mode/blob/master/test/clojure-mode-indentation-test.el -(defmacro check-indentation (description before after &optional var-bindings) - "Declare an ert test for indentation behaviour. -The test will check that the swift indentation command changes the buffer -from one state to another. It will also test that point is moved to an -expected position. -DESCRIPTION is a symbol describing the test. -BEFORE is the buffer string before indenting, where a pipe (|) represents -point. -AFTER is the expected buffer string after indenting, where a pipe (|) -represents the expected position of point. -VAR-BINDINGS is an optional let-bindings list. It can be used to set the -values of customisable variables." - (declare (indent 1)) - (let ((fname (intern (format "indentation/%s" description)))) - `(ert-deftest ,fname () - (let* ((after ,after) - (expected-cursor-pos (1+ (s-index-of "|" after))) - (expected-state (delete ?| after)) - ,@var-bindings) - (with-temp-buffer - ;; fix the JAR location prior to mode initialization - ;; for some reason, plantuml-mode disregards the setq-local - (setq-local plantuml-jar-path plantuml-test-jar-path) - (plantuml-init-once) - (add-text-and-position-cursor ,before) - (plantuml-mode) - - ;; use 2 spaces instead of one tab for indentation - (setq-local indent-tabs-mode nil) - (setq-local tab-width 2) - (indent-according-to-mode) - - (should (equal expected-state (buffer-string))) - (should (equal expected-cursor-pos (point)))))))) - -(check-indentation toplevel-relationship - "|Nobody -> [APIGateway]" - "|Nobody -> [APIGateway]") - -(check-indentation package-block - "package APackage { -|A -> B -}" - "package APackage { - |A -> B -}") - -(check-indentation nested-package - "package APackage { -|package AnotherPackage { -} -}" - "package APackage { - |package AnotherPackage { -} -}") - -(check-indentation empty-package - "|package Foo {}" - "|package Foo {}") - -(check-indentation relative-indent - "package APackage { -database Foo -|A --> B -} -}" - "package APackage { -database Foo - |A --> B -} -}" - ) - -(check-indentation note-as - "note as N1 -|This is a note -end note" - "note as N1 - |This is a note -end note" - ) - -(check-indentation note-of - "note right of Foo -|This is a note -end note" - "note right of Foo - |This is a note -end note" - ) - -(check-indentation alt - "alt choice 1 -|A -> B -end +(ert-deftest plantuml-test-block-indentation/note-as () + "Test correct indentation of a note-as block." + (plantuml-test-indent-block + " +note as N1 +This is a note +end note " - "alt choice 1 - |A -> B -end -") - -(check-indentation alt-end - "alt choice 1 - A -> B -|end + " +note as N1 + This is a note +end note " - "alt choice 1 - A -> B -|end -") + )) -(check-indentation alt-else - "alt choice 1 -|else -end +(ert-deftest plantuml-test-block-indentation/activate-deactivate () + "Test correct indentation of an activate-deactivate block." + (plantuml-test-indent-block + " +activate participant_1 +participant_1 -> participant_2 : f() +deactivate participant_1 " - "alt choice 1 -|else -end -") + " +activate participant_1 + participant_1 -> participant_2 : f() +deactivate participant_1 +")) -(check-indentation alt-else-body - "alt choice 1 -else -|A -> B -end +(ert-deftest plantuml-test-block-indentation/activate-deactivate-nested () + "Test correct indentation of two nested activate-deactivate blocks." + (plantuml-test-indent-block + " +activate participant_1 +activate participant_2 +participant_1 -> participant_2 : f() +deactivate participant_2 +deactivate participant_1 " - "alt choice 1 -else - |A -> B -end -") - -(check-indentation alt-else-end - "alt choice 1 -else -|end -" - "alt choice 1 -else -|end -") - -(check-indentation alt-else-body - "alt choice 1 -else -|A -> B -end -" - "alt choice 1 -else - |A -> B -end -") + " +activate participant_1 + activate participant_2 + participant_1 -> participant_2 : f() + deactivate participant_2 +deactivate participant_1 +")) (provide 'plantuml-indentation-test) -;;; plantuml-mode-preview-test.el ends here +;;; plantuml-indentation-test.el ends here