Rsch/indentation/fix and enhance (#85)

* fixed indentation code and added de-/activate to indentation

* added tests for indentation changes

* changed indentation code and tests

- more changes to plantuml-indent-regexs
- added more test
- changed function to determine indentation level and tests accordingly
- changed plantuml-indent-line and tests accordingly
- changed test names and doc strings to be more consistent

* more indentation tests, corrected regex for par block

- the par block usually does not have any label
- added database to start regex and added a test
- added block tests
This commit is contained in:
ReneSchmelzer 2019-01-07 21:23:04 +01:00 committed by Carlo Sciolla
parent 49f707b20c
commit ae97b806bc
2 changed files with 485 additions and 190 deletions

View File

@ -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-keywords-regexp (concat "^\\s *" (regexp-opt plantuml-keywords 'words) "\\|\\(<\\|<|\\|\\*\\|o\\)\\(\\.+\\|-+\\)\\|\\(\\.+\\|-+\\)\\(>\\||>\\|\\*\\|o\\)\\|\\.\\{2,\\}\\|-\\{2,\\}"))
(defvar plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words)) (defvar plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words))
(defvar plantuml-preprocessors-regexp (concat "^\\s *" (regexp-opt plantuml-preprocessors '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-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\\|.*}\\)") (defvar plantuml-indent-regexp-end "^[ \t]*\\(endif\\|else\\|end\\|end\s+note\\|.*}\\|deactivate\s+.+\\)")
(setq plantuml-font-lock-keywords (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-start)
(defvar plantuml-indent-regexp-end) (defvar plantuml-indent-regexp-end)
(save-excursion (save-excursion
(let ((relative-depth 0) (let ((relative-depth 0))
(bob-visited? nil)) ;; current line
(beginning-of-line) (beginning-of-line)
(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) (forward-line -1)
(while (and (>= relative-depth 0)
(not bob-visited?))
(if (bobp)
(setq bob-visited? t))
(if (looking-at plantuml-indent-regexp-end) (if (looking-at plantuml-indent-regexp-end)
(setq relative-depth (1- relative-depth))) (setq relative-depth (1- relative-depth)))
(if (looking-at plantuml-indent-regexp-start) (if (looking-at plantuml-indent-regexp-start)
(setq relative-depth (1+ relative-depth))) (setq relative-depth (1+ relative-depth))))
(forward-line -1))
(if (<= relative-depth 0) (if (<= relative-depth 0)
0 0
relative-depth)))) relative-depth))))
(defun plantuml-indent-line () (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) (interactive)
;; forward declare the lazy initialized constants ;; forward declare the lazy initialized constants
(defvar plantuml-indent-regexp-start) (defvar plantuml-indent-regexp-start)
(defvar plantuml-indent-regexp-end) (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 (save-excursion
(beginning-of-line) (beginning-of-line)
(if (bobp) (indent-line-to (* tab-width (plantuml-current-block-depth))))
(indent-line-to 0)
(let ((depth (plantuml-current-block-depth))) ;; restore position in text of line
(when (looking-at plantuml-indent-regexp-end) (goto-char (- (line-end-position) original-position-eol))))
(setq depth (max (1- depth) 0)))
(indent-line-to (* tab-width depth)))))
(forward-char (- (current-indentation)
original-indentation))))
;;;###autoload ;;;###autoload

View File

@ -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) ;; Author: Raymond Huang (rymndhng)
;; Maintainer: Carlo Sciolla (skuro) ;; Maintainer: Carlo Sciolla (skuro)
@ -10,71 +10,166 @@
;;; Code: ;;; Code:
(defun add-text-and-position-cursor (txt) (defun plantuml-test-add-text-and-position-cursor (txt)
"Add TXT into the buffer, move cursor to the position of the marker | and delete the marker." "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) (insert txt)
(goto-char (point-min)) (goto-char (point-min))
(search-forward "|") (search-forward "|")
(delete-char -1)) (delete-char -1))
(defun assert-block-depth (expected txt) (defun plantuml-test-assert-block-depth (expected txt)
"Assert the EXPECTED indentation level for the given TXT." "Test helper for `plantuml-mode' tests.
Assert the EXPECTED indentation level for the given TXT."
(with-temp-buffer (with-temp-buffer
(add-text-and-position-cursor txt) (plantuml-test-add-text-and-position-cursor txt)
(let ((actual (plantuml-current-block-depth))) (let ((actual (plantuml-current-block-depth)))
(should (equal expected actual))))) (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) (setq-local plantuml-jar-path plantuml-test-jar-path)
(plantuml-init-once) (plantuml-init-once)
(assert-block-depth 0 " (plantuml-test-assert-block-depth 0 "|
A |-> B") activate p1
activate p2
foo
deactivate p2
deactivate p1
")
)
(assert-block-depth 0 " (ert-deftest plantuml-test-current-block-depth_0 ()
pac|kage Foo { "Test `plantuml-current-block-depth' level 0 at beginning of first line."
A -> B (setq-local plantuml-jar-path plantuml-test-jar-path)
}") (plantuml-init-once)
(assert-block-depth 1 " (plantuml-test-assert-block-depth 0 "
package APackage { |activate p1
|A -> B 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)
(assert-block-depth 1 " (plantuml-test-assert-block-depth 0 "
alt choice 1 acti|vate p1
|A -> B activate p2
end foo
")) deactivate p2
deactivate p1
")
)
;; This is taken from https://github.com/clojure-emacs/clojure-mode/blob/master/test/clojure-mode-indentation-test.el (ert-deftest plantuml-test-current-block-depth_2 ()
(defmacro check-indentation (description before after &optional var-bindings) "Test `plantuml-current-block-depth' level 0 at end of first line"
"Declare an ert test for indentation behaviour. (setq-local plantuml-jar-path plantuml-test-jar-path)
The test will check that the swift indentation command changes the buffer (plantuml-init-once)
from one state to another. It will also test that point is moved to an
expected position. (plantuml-test-assert-block-depth 0 "
DESCRIPTION is a symbol describing the test. activate p1|
BEFORE is the buffer string before indenting, where a pipe (|) represents activate p2
point. foo
AFTER is the expected buffer string after indenting, where a pipe (|) deactivate p2
represents the expected position of point. deactivate p1
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 plantuml-test-current-block-depth_3 ()
`(ert-deftest ,fname () "Test `plantuml-current-block-depth' level 1 at beginning of 2nd line."
(let* ((after ,after)
(expected-cursor-pos (1+ (s-index-of "|" after))) (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)) (expected-state (delete ?| after))
,@var-bindings) )
(with-temp-buffer (with-temp-buffer
;; fix the JAR location prior to mode initialization ;; fix the JAR location prior to mode initialization
;; for some reason, plantuml-mode disregards the setq-local ;; for some reason, plantuml-mode disregards the setq-local
(setq-local plantuml-jar-path plantuml-test-jar-path) (setq-local plantuml-jar-path plantuml-test-jar-path)
(plantuml-init-once) (plantuml-init-once)
(add-text-and-position-cursor ,before) (plantuml-test-add-text-and-position-cursor before)
(plantuml-mode) (plantuml-mode)
;; use 2 spaces instead of one tab for indentation ;; use 2 spaces instead of one tab for indentation
@ -83,129 +178,331 @@ values of customisable variables."
(indent-according-to-mode) (indent-according-to-mode)
(should (equal expected-state (buffer-string))) (should (equal expected-state (buffer-string)))
(should (equal expected-cursor-pos (point)))))))) (should (equal expected-cursor-pos (point))))))
(check-indentation toplevel-relationship (ert-deftest plantuml-test-line-indentation/empty-line-l0 ()
"|Nobody -> [APIGateway]" "Test correct indentation of empty line - indentation level 0."
"|Nobody -> [APIGateway]") (plantuml-test-indent-line "|" "|"))
(check-indentation package-block (ert-deftest plantuml-test-line-indentation/bol-notindent-l0 ()
"package APackage { "Test correct indentation of a not indented line with point at beginning of line - indentation level 0."
|A -> B (plantuml-test-indent-line "|participant A"
}" "|participant A"))
"package APackage {
|A -> B
}")
(check-indentation nested-package (ert-deftest plantuml-test-line-indentation/mol-notindent-l0 ()
"package APackage { "Test correct indentation of a not indented line with point at middle of line - indentation level 0."
|package AnotherPackage { (plantuml-test-indent-line "parti|cipant"
} "parti|cipant"))
}"
"package APackage {
|package AnotherPackage {
}
}")
(check-indentation empty-package (ert-deftest plantuml-test-line-indentation/eol-notindent-l0 ()
"|package Foo {}" "Test correct indentation of a not indented line with point at end of line - indentation level 0."
"|package Foo {}") (plantuml-test-indent-line "participant A|"
"participant A|"))
(check-indentation relative-indent (ert-deftest plantuml-test-line-indentation/bol-indented-l0 ()
"package APackage { "Test correct indentation of an indented line with point at beginning of line - indentation level 0."
database Foo (plantuml-test-indent-line " |participant A"
|A --> B "|participant A"))
}
}"
"package APackage {
database Foo
|A --> B
}
}"
)
(check-indentation note-as (ert-deftest plantuml-test-line-indentation/mol-indented-l0 ()
"note as N1 "Test correct indentation of an indented line with point at middle of line - indentation level 0."
|This is a note (plantuml-test-indent-line " parti|cipant"
end note" "parti|cipant"))
"note as N1
|This is a note
end note"
)
(check-indentation note-of (ert-deftest plantuml-test-line-indentation/eol-indented-l0 ()
"note right of Foo "Test correct indentation of an indented line with point at end of line - indentation level 0."
|This is a note (plantuml-test-indent-line " participant A|"
end note" "participant A|"))
"note right of Foo
|This is a note
end note"
)
(check-indentation alt (ert-deftest plantuml-test-line-indentation/empty-line-l1 ()
"alt choice 1 "Test correct indentation of empty line - indentation level 1."
|A -> B (plantuml-test-indent-line
end "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
" "
"alt choice 1 package APackage ()
|A -> B "
end "
") package APackage ()
"))
(check-indentation alt-end (ert-deftest plantuml-test-block-indentation/package ()
"alt choice 1 "Test correct indentation of a package block."
(plantuml-test-indent-block
"
package APackage {
A -> B A -> B
|end }
" "
"alt choice 1 "
package APackage {
A -> B A -> B
|end }
") "))
(check-indentation alt-else (ert-deftest plantuml-test-block-indentation/package-database-nested ()
"alt choice 1 "Test correct indentation of two nested blocks, a package and a database.
|else Note: currently the inner database is not indented."
(plantuml-test-indent-block
"
package APackage {
database ADatabase {
A -> B
}
}
"
"
package APackage {
database ADatabase {
A -> B
}
}
"))
(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
end end
" "
"alt choice 1 "
|else alt choice 1
A -> B
end end
") " ))
(check-indentation alt-else-body (ert-deftest plantuml-test-block-indentation/alt-else-end ()
"alt choice 1 "Test correct indentation of an alt-else-end block."
(plantuml-test-indent-block
"
alt choice 1
A -> B
else else
|A -> B B -> C
end end
" "
"alt choice 1
else
|A -> B
end
")
(check-indentation alt-else-end
"alt choice 1
else
|end
" "
"alt choice 1 alt choice 1
A -> B
else else
|end B -> C
") end
" ))
(check-indentation alt-else-body (ert-deftest plantuml-test-block-indentation/opt ()
"alt choice 1 "Test correct indentation of an opt block."
else (plantuml-test-indent-block
|A -> B "
opt event triggered
A -> B
end end
" "
"alt choice 1 "
else opt event triggered
|A -> B A -> B
end 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
"))
(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
"
"
note as N1
This is a note
end note
"
))
(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
"
"
activate participant_1
participant_1 -> participant_2 : f()
deactivate participant_1
"))
(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
"
"
activate participant_1
activate participant_2
participant_1 -> participant_2 : f()
deactivate participant_2
deactivate participant_1
"))
(provide 'plantuml-indentation-test) (provide 'plantuml-indentation-test)
;;; plantuml-mode-preview-test.el ends here ;;; plantuml-indentation-test.el ends here