[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/lua-mode 51a32ef 417/468: Merge pull request #176 from imm
From: |
Philip Kaludercic |
Subject: |
[nongnu] elpa/lua-mode 51a32ef 417/468: Merge pull request #176 from immerrr/improve-indentation-for-continuation-lines |
Date: |
Thu, 5 Aug 2021 04:59:20 -0400 (EDT) |
branch: elpa/lua-mode
commit 51a32ef86cb757b225cd85677853a16df3c234c6
Merge: 0781d83 b170500
Author: immerrr again <immerrr+lua@gmail.com>
Commit: GitHub <noreply@github.com>
Merge pull request #176 from
immerrr/improve-indentation-for-continuation-lines
Improve indentation for continuation lines
---
lua-mode.el | 352 ++++++++----
test/indentation-tests/README.md | 63 ++
test/indentation-tests/assignment-indentation.lua | 188 ++++++
test/indentation-tests/continuation-lines.lua | 181 ++++++
test/indentation-tests/do-block.lua | 30 +
test/indentation-tests/for-equals-block.lua | 66 +++
test/indentation-tests/for-in-block.lua | 86 +++
test/indentation-tests/goto-label.lua | 126 ++++
test/indentation-tests/if-elseif-else-block.lua | 240 ++++++++
test/indentation-tests/issue-33.lua | 41 ++
test/indentation-tests/only-use-last-opener.lua | 97 ++++
test/indentation-tests/repeat-until-block.lua | 57 ++
test/indentation-tests/smoke.lua | 3 +
test/indentation-tests/while-block.lua | 95 +++
test/test-indentation.el | 669 +++-------------------
test/utils.el | 52 +-
16 files changed, 1632 insertions(+), 714 deletions(-)
diff --git a/lua-mode.el b/lua-mode.el
index f7b1513..f1640b9 100644
--- a/lua-mode.el
+++ b/lua-mode.el
@@ -132,11 +132,12 @@
(seq (* digit) (opt ".") (+ digit)))
(opt (regexp "[eE][+-]?[0-9]+"))))
(lua-assignment-op (seq "=" (or buffer-end (not (any "=")))))
- (lua-token (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<"
- ">" "=" ";" ":" "," "." ".." "..."))
+ (lua-operator (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">="
"<"
+ ">" "=" ";" ":" "," "." ".." "..."))
+ (lua-keyword-operator (symbol "and" "not" "or"))
(lua-keyword
- (symbol "and" "break" "do" "else" "elseif" "end" "for" "function"
- "goto" "if" "in" "local" "not" "or" "repeat" "return"
+ (symbol "break" "do" "else" "elseif" "end" "for" "function"
+ "goto" "if" "in" "local" "repeat" "return"
"then" "until" "while"))))
(defmacro lua-rx (&rest regexps)
@@ -214,14 +215,15 @@ element is itself expanded with `lua-rx-to-string'. "
(opt (regexp "[eE][+-]?[0-9]+"))))
(lua-assignment-op
:rx (seq "=" (or buffer-end (not (any "=")))))
- (lua-token
+ (lua-operator
:rx (or "+" "-" "*" "/" "%" "^" "#" "==" "~=" "<=" ">=" "<"
">" "=" ";" ":" "," "." ".." "..."))
+ (lua-keyword-operator
+ :rx (symbol "and" "not" "or"))
(lua-keyword
- :rx (symbol "and" "break" "do" "else" "elseif" "end" "for"
"function"
- "goto" "if" "in" "local" "not" "or" "repeat"
"return"
- "then" "until" "while")))
- ))))
+ :rx (symbol "break" "do" "else" "elseif" "end" "for" "function"
+ "goto" "if" "in" "local" "repeat" "return"
+ "then" "until" "while")))))))
;; Local variables
@@ -404,21 +406,24 @@ Usually, stdin:XX line number points to nowhere."
"If non-nil, contents of multiline string will be indented.
Otherwise leading amount of whitespace on each line is preserved."
:group 'lua
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom lua-indent-nested-block-content-align t
"If non-nil, the contents of nested blocks are indented to
align with the column of the opening parenthesis, rather than
just forward by `lua-indent-level'."
:group 'lua
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom lua-indent-close-paren-align t
"If non-nil, close parenthesis are aligned with their open
parenthesis. If nil, close parenthesis are aligned to the
beginning of the line."
:group 'lua
- :type 'boolean)
+ :type 'boolean
+ :safe #'booleanp)
(defcustom lua-jump-on-traceback t
"*Jump to innermost traceback location in *lua* buffer. When this
@@ -619,7 +624,7 @@ Groups 6-9 can be used in any of argument regexps."
. font-lock-constant-face)
;; Keywords
- (,(lua-rx lua-keyword)
+ (, (lua-rx (or lua-keyword lua-keyword-operator))
. font-lock-keyword-face)
;; Labels used by the "goto" statement
@@ -1212,17 +1217,17 @@ Returns final value of point as integer or nil if
operation failed."
(defconst lua-cont-eol-regexp
(eval-when-compile
(concat
- "\\(\\_<"
+ "\\(?:\\(?1:\\_<"
(regexp-opt '("and" "or" "not" "in" "for" "while"
"local" "function" "if" "until" "elseif" "return")
t)
- "\\_>\\|"
- "\\(^\\|[^" lua-operator-class "]\\)"
+ "\\_>\\)\\|"
+ "\\(?:^\\|[^" lua-operator-class "]\\)\\(?2:"
(regexp-opt '("+" "-" "*" "/" "%" "^" ".." "=="
"=" "<" ">" "<=" ">=" "~=" "." ":"
- "&" "|" "~" ">>" "<<" "~")
+ "&" "|" "~" ">>" "<<" "~" ",")
t)
- "\\)"
+ "\\)\\)"
"\\s *\\="))
"Regexp that matches the ending of a line that needs continuation.
@@ -1234,14 +1239,14 @@ an optional whitespace till the end of the line.")
(eval-when-compile
(concat
"\\=\\s *"
- "\\(\\_<"
+ "\\(?:\\(?1:\\_<"
(regexp-opt '("and" "or" "not") t)
- "\\_>\\|"
- (regexp-opt '("+" "-" "*" "/" "%" "^" ".." "=="
+ "\\_>\\)\\|\\(?2:"
+ (regexp-opt '("," "+" "-" "*" "/" "%" "^" ".." "=="
"=" "<" ">" "<=" ">=" "~=" "." ":"
"&" "|" "~" ">>" "<<" "~")
t)
- "\\($\\|[^" lua-operator-class "]\\)"
+ "\\)\\(?:$\\|[^" lua-operator-class "]\\)"
"\\)"))
"Regexp that matches a line that continues previous one.
@@ -1253,7 +1258,8 @@ previous one even though it looked like an
end-of-statement.")
(defun lua-last-token-continues-p ()
"Return non-nil if the last token on this line is a continuation token."
(let ((line-begin (line-beginning-position))
- (line-end (line-end-position)))
+ (line-end (line-end-position))
+ return-value)
(save-excursion
(end-of-line)
;; we need to check whether the line ends in a comment and
@@ -1262,7 +1268,30 @@ previous one even though it looked like an
end-of-statement.")
(if (looking-at "--")
(setq line-end (point))))
(goto-char line-end)
- (re-search-backward lua-cont-eol-regexp line-begin t))))
+ (setq return-value (and (re-search-backward lua-cont-eol-regexp
line-begin t)
+ (or (match-beginning 1)
+ (match-beginning 2))))
+ (if (and return-value
+ (string-equal (match-string-no-properties 0) "return"))
+ ;; "return" keyword is ambiguous and depends on next token
+ (unless (save-excursion
+ (goto-char (match-end 0))
+ (forward-comment (point-max))
+ (and
+ ;; Not continuing: at end of file
+ (not (eobp))
+ (or
+ ;; "function" keyword: it is a continuation, e.g.
+ ;;
+ ;; return
+ ;; function() return 123 end
+ ;;
+ (looking-at (lua-rx (symbol "function")))
+ ;; Looking at semicolon or any other keyword: not
continuation
+ (not (looking-at (lua-rx (or ";" lua-keyword)))))))
+ (setq return-value nil)))
+ return-value)))
+
(defun lua-first-token-continues-p ()
"Return non-nil if the first token on this line is a continuation token."
@@ -1272,22 +1301,18 @@ previous one even though it looked like an
end-of-statement.")
;; if first character of the line is inside string, it's a continuation
;; if strings aren't supposed to be indented,
`lua-calculate-indentation' won't even let
;; the control inside this function
- (re-search-forward lua-cont-bol-regexp line-end t))))
+ (and
+ (re-search-forward lua-cont-bol-regexp line-end t)
+ (or (match-beginning 1)
+ (match-beginning 2))))))
-(defconst lua-block-starter-regexp
- (eval-when-compile
- (concat
- "\\(\\_<"
- (regexp-opt '("do" "while" "repeat" "until" "if" "then"
- "else" "elseif" "end" "for" "local") t)
- "\\_>\\)")))
-(defun lua-first-token-starts-block-p ()
- "Return non-nil if the first token on this line is a block starter token."
- (let ((line-end (line-end-position)))
- (save-excursion
- (beginning-of-line)
- (re-search-forward (concat "\\s *" lua-block-starter-regexp) line-end
t))))
+(defun lua--backward-up-list-noerror ()
+ "Safe version of lua-backward-up-list that does not signal an error."
+ (condition-case nil
+ (lua-backward-up-list)
+ (scan-error nil)))
+
(defun lua-backward-up-list ()
"Goto starter/opener of the block that contains point."
@@ -1306,8 +1331,9 @@ previous one even though it looked like an
end-of-statement.")
;; If the token is a close token, continue to skip its opener. If not
;; close, stop and return found token.
while (eq token-type 'close)
- ;; Find matching opener to skip it and continue from beginning. Return
nil
- ;; on failure.
+ ;; Find matching opener to skip it and continue from beginning.
+ ;;
+ ;; Return nil on failure.
always (let ((position (lua-find-matching-token-word token 'backward)))
(and position (goto-char position)))
finally return token-info)
@@ -1320,8 +1346,8 @@ previous one even though it looked like an
end-of-statement.")
(if (eql start-pos end-pos) start-pos (match-beginning 0))
(if (eql start-pos end-pos) start-pos (match-end 0))))))))
-(defun lua-is-continuing-statement-p (&optional parse-start)
- "Return non-nil if the line at PARSE-START continues a statement.
+(defun lua-is-continuing-statement-p-1 ()
+ "Return non-nil if current lined continues a statement.
More specifically, return the point in the line that is continued.
The criteria for a continuing statement are:
@@ -1329,17 +1355,70 @@ The criteria for a continuing statement are:
* the last token of the previous line is a continuing op,
OR the first token of the current line is a continuing op
+* the expression is not enclosed by a parentheses/braces/brackets"
+ (let (prev-line continuation-pos parent-block-opener)
+ (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back)))
+ (and prev-line
+ (or
+ ;; Binary operator or keyword that implies continuation.
+ (save-excursion
+ (and (setq continuation-pos
+ (or (lua-first-token-continues-p)
+ (save-excursion (and (goto-char prev-line)
+ ;; check last token of
previous nonblank line
+
(lua-last-token-continues-p)))))
+ (not
+ ;; Operators/keywords does not create continuation inside
some blocks:
+ (and
+ (setq parent-block-opener (car-safe
(lua--backward-up-list-noerror)))
+ (or
+ ;; - inside parens/brackets
+ (member parent-block-opener '("(" "["))
+ ;; - inside braces if it is a comma
+ (and (eq (char-after continuation-pos) ?,)
+ (equal parent-block-opener "{")))))
+ continuation-pos))
+ ;; "for" expressions (until the next do) imply continuation.
+ (when (string-equal (car-safe (lua--backward-up-list-noerror)) "for")
+ (point))))))
+
+
+
+(defun lua-is-continuing-statement-p (&optional parse-start)
+ "Returns non-nil if the line at PARSE-START should be indented as
continuation line.
+
+This true is when the line :
+
+* is continuing a statement itself
+
+* starts with a 1+ block-closer tokens, an top-most block opener is on a
continuation line
"
- (let ((prev-line nil))
- (save-excursion
- (if parse-start (goto-char parse-start))
- (save-excursion (setq prev-line (lua-forward-line-skip-blanks 'back)))
- (and prev-line
- (not (lua-first-token-starts-block-p))
- (or (lua-first-token-continues-p)
- (and (goto-char prev-line)
- ;; check last token of previous nonblank line
- (lua-last-token-continues-p)))))))
+ (save-excursion
+ (if parse-start (goto-char parse-start))
+
+ ;; If line starts with a series of closer tokens, whether or not the line
+ ;; is a continuation line is decided by the opener line, e.g.
+ ;;
+ ;; x = foo +
+ ;; long_function_name(
+ ;; long_parameter_1,
+ ;; long_parameter_2,
+ ;; long_parameter_3,
+ ;; ) + long_function_name2({
+ ;; long_parameter_1,
+ ;; long_parameter_2,
+ ;; long_parameter_3,
+ ;; })
+ ;;
+ ;; Final line, "})" is a continuation line, but it is decided by the
+ ;; opener line, ") + long_function_name2({", which in its turn is decided
+ ;; by the "long_function_name(" line, which is a continuation line
+ ;; because the line before it ends with a binary operator.
+ (while (and (lua--goto-line-beginning-rightmost-closer)
+ (lua--backward-up-list-noerror)
+ (lua-is-continuing-statement-p-1)))
+ (lua-is-continuing-statement-p-1)))
+
(defun lua-make-indentation-info-pair (found-token found-pos)
"Create a pair from FOUND-TOKEN and FOUND-POS for indentation calculation.
@@ -1380,9 +1459,10 @@ Don't use standalone."
;; nullify a previous then if on the same line.
((member found-token (list "until" "elseif"))
(save-excursion
- (let ((line (line-number-at-pos)))
- (if (and (lua-goto-matching-block-token found-pos 'backward)
- (= line (line-number-at-pos)))
+ (let* ((line-beginning (line-beginning-position))
+ (same-line (and (lua-goto-matching-block-token found-pos
'backward)
+ (<= line-beginning (point)))))
+ (if same-line
(cons 'remove-matching 0)
(cons 'relative 0)))))
@@ -1391,24 +1471,29 @@ Don't use standalone."
;; either the next line will be indented correctly, or the end on the same
;; line will remove the effect of the else.
((string-equal found-token "else")
- (save-excursion
- (let ((line (line-number-at-pos)))
- (if (and (lua-goto-matching-block-token found-pos 'backward)
- (= line (line-number-at-pos)))
- (cons 'replace-matching (cons 'relative lua-indent-level))
- (cons 'relative lua-indent-level)))))
+ (save-excursion
+ (let* ((line-beginning (line-beginning-position))
+ (same-line (and (lua-goto-matching-block-token found-pos
'backward)
+ (<= line-beginning (point)))))
+ (if same-line
+ (cons 'replace-matching (cons 'relative lua-indent-level))
+ (cons 'relative lua-indent-level)))))
;; Block closers. If they are on the same line as their openers, they simply
;; eat up the matching indentation modifier. Otherwise, they pull
;; indentation back to the matching block opener.
((member found-token (list ")" "}" "]" "end"))
(save-excursion
- (let ((line (line-number-at-pos)))
- (lua-goto-matching-block-token found-pos 'backward)
- (if (/= line (line-number-at-pos))
+ (let* ((line-beginning (line-beginning-position))
+ (same-line (and (lua-goto-matching-block-token found-pos
'backward)
+ (<= line-beginning (point)))))
+ (if (not same-line)
(lua-calculate-indentation-info (point))
(cons 'remove-matching 0)))))
+ ((member found-token '("do" "then"))
+ `(multiple . ((cancel-continued-line . nil) (relative .
,lua-indent-level))))
+
;; Everything else. This is from the original code: If opening a block
;; (match-data 1 exists), then push indentation one level up, if it is
;; closing a block, pull it one level down.
@@ -1419,8 +1504,8 @@ Don't use standalone."
;; end of a block matched
(- lua-indent-level))))))
-(defun lua-add-indentation-info-pair (pair info)
- "Add the given indentation info PAIR to the list of indentation INFO.
+(defun lua-add-indentation-info-pair (pair info-list)
+ "Add the given indentation info PAIR to the list of indentation INFO-LIST.
This function has special case handling for two tokens: remove-matching,
and replace-matching. These two tokens are cleanup tokens that remove or
alter the effect of a previously recorded indentation info.
@@ -1434,17 +1519,27 @@ and the cdr of the replace-matching info is added in
its place. This is used
when a middle-of the block (the only case is 'else') is seen on the same line
the block is opened."
(cond
+ ( (eq 'multiple (car pair))
+ (let ((info-pair-elts (cdr pair)))
+ (while info-pair-elts
+ (setq info-list (lua-add-indentation-info-pair (car info-pair-elts)
info-list)
+ info-pair-elts (cdr info-pair-elts)))
+ info-list))
+ ( (eq 'cancel-continued-line (car pair))
+ (if (eq (caar info-list) 'continued-line)
+ (cdr info-list)
+ info-list))
( (eq 'remove-matching (car pair))
- ; Remove head of list
- (cdr info))
+ ;; Remove head of list
+ (cdr info-list))
( (eq 'replace-matching (car pair))
- ; remove head of list, and add the cdr of pair instead
- (cons (cdr pair) (cdr info)))
+ ;; remove head of list, and add the cdr of pair instead
+ (cons (cdr pair) (cdr info-list)))
( (listp (cdr-safe pair))
- (nconc pair info))
+ (nconc pair info-list))
( t
- ; Just add the pair
- (cons pair info))))
+ ;; Just add the pair
+ (cons pair info-list))))
(defun lua-calculate-indentation-info-1 (indentation-info bound)
"Helper function for `lua-calculate-indentation-info'.
@@ -1467,10 +1562,11 @@ The effect of each token can be either a shift relative
to the current
indentation level, or indentation to some absolute column. This information
is collected in a list of indentation info pairs, which denote absolute
and relative each, and the shift/column to indent to."
- (let (indentation-info)
-
- (while (lua-is-continuing-statement-p)
- (lua-forward-line-skip-blanks 'back))
+ (let (indentation-info cont-stmt-pos)
+ (while (setq cont-stmt-pos (lua-is-continuing-statement-p))
+ (lua-forward-line-skip-blanks 'back)
+ (when (< cont-stmt-pos (point))
+ (goto-char cont-stmt-pos)))
;; calculate indentation modifiers for the line itself
(setq indentation-info (list (cons 'absolute (current-indentation))))
@@ -1492,7 +1588,7 @@ and relative each, and the shift/column to indent to."
;; if it's the first non-continued line, subtract one level
(when (eq (car (car indentation-info)) 'continued-line)
- (pop indentation-info)))
+ (push (cons 'stop-continued-line (- lua-indent-level))
indentation-info)))
;; add modifiers found in this continuation line
(setq indentation-info
@@ -1502,19 +1598,29 @@ and relative each, and the shift/column to indent to."
indentation-info))
-(defun lua-accumulate-indentation-info (info)
+(defun lua-accumulate-indentation-info (reversed-indentation-info)
"Accumulates the indentation information previously calculated by
lua-calculate-indentation-info. Returns either the relative indentation
shift, or the absolute column to indent to."
- (let ((info-list (reverse info))
+ (let (indentation-info
(type 'relative)
(accu 0))
+ ;; Aggregate all neighbouring relative offsets, reversing the INFO list.
+ (cl-dolist (elt reversed-indentation-info)
+ (if (and (eq (car elt) 'relative)
+ (eq (caar indentation-info) 'relative))
+ (setcdr (car indentation-info) (+ (cdar indentation-info) (cdr elt)))
+ (push elt indentation-info)))
+
+ ;; Aggregate indentation info, taking 'absolute modifiers into account.
(mapc (lambda (x)
- (setq accu (if (eq 'absolute (car x))
- (progn (setq type 'absolute)
- (cdr x))
- (+ accu (cdr x)))))
- info-list)
+ (let ((new-val (cdr x)))
+ (if (eq 'absolute (car x))
+ (progn (setq type 'absolute
+ accu new-val))
+ (setq accu (+ accu new-val)))))
+ indentation-info)
+
(cons type accu)))
(defun lua-calculate-indentation-block-modifier (&optional parse-end)
@@ -1564,6 +1670,7 @@ one."
;; right hand side
(or "{"
"function"
+ "("
(seq (group-n 1 (eval lua--function-name-rx) (* blank))
(any "({")))))))
@@ -1605,6 +1712,27 @@ left-shifter expression. "
(looking-at lua--left-shifter-regexp)
(= old-point (match-end 1))))))
+(defun lua--goto-line-beginning-rightmost-closer (&optional parse-start)
+ (let (case-fold-search pos line-end-pos return-val)
+ (save-excursion
+ (if parse-start (goto-char parse-start))
+ (setq line-end-pos (line-end-position))
+ (back-to-indentation)
+ (unless (lua-comment-or-string-p)
+ (cl-loop while (and (<= (point) line-end-pos)
+ (looking-at lua-indentation-modifier-regexp))
+ for token-info = (lua-get-block-token-info (match-string 0))
+ for token-type = (lua-get-token-type token-info)
+ while (not (eq token-type 'open))
+ do (progn
+ (setq pos (match-beginning 0)
+ return-val token-info)
+ (goto-char (match-end 0))
+ (forward-comment (line-end-position))))))
+ (when pos
+ (progn
+ (goto-char pos)
+ return-val))))
(defun lua-calculate-indentation-override (&optional parse-start)
@@ -1616,27 +1744,43 @@ line containing block-open token for the last
block-close token
in the sequence.
If not, return nil."
- (let (case-fold-search token-info block-token-pos)
+ (let (case-fold-search rightmost-closer-info opener-info opener-pos)
(save-excursion
- (if parse-start (goto-char parse-start))
+ (when (and (setq rightmost-closer-info
(lua--goto-line-beginning-rightmost-closer parse-start))
+ (setq opener-info (lua--backward-up-list-noerror))
+ ;; Ensure opener matches closer.
+ (string-match (lua-get-token-match-re rightmost-closer-info
'backward)
+ (car opener-info)))
+
+ ;; Special case: "middle" tokens like for/do, while/do, if/then,
+ ;; elseif/then: corresponding "end" or corresponding "else" must be
+ ;; unindented to the beginning of the statement, which is not
+ ;; necessarily the same as beginning of string that contains "do", e.g.
+ ;;
+ ;; while (
+ ;; foo and
+ ;; bar) do
+ ;; hello_world()
+ ;; end
+ (setq opener-pos (point))
+ (unless (or
+ (and (string-equal (car opener-info) "do")
+ (member (car (lua--backward-up-list-noerror)) '("while"
"for")))
+ (and (string-equal (car opener-info) "then")
+ (member (car (lua--backward-up-list-noerror)) '("if"
"elseif"))))
+ (goto-char opener-pos))
+
+ ;; (let (cont-stmt-pos)
+ ;; (while (setq cont-stmt-pos (lua-is-continuing-statement-p))
+ ;; (goto-char cont-stmt-pos)))
+ ;; Exception cases: when the start of the line is an assignment,
+ ;; go to the start of the assignment instead of the matching item
+ (if (and lua-indent-close-paren-align
+ (member (car opener-info) '("{" "(" "["))
+ (not (lua-point-is-after-left-shifter-p)))
+ (current-column)
+ (current-indentation))))))
- (back-to-indentation)
- (unless (lua-comment-or-string-p)
- (while
- (and (looking-at lua-indentation-modifier-regexp)
- (setq token-info (lua-get-block-token-info (match-string 0)))
- (not (eq 'open (lua-get-token-type token-info))))
- (setq block-token-pos (match-beginning 0))
- (goto-char (match-end 0))
- (skip-syntax-forward " " (line-end-position)))
-
- (when (lua-goto-matching-block-token block-token-pos 'backward)
- ;; Exception cases: when the start of the line is an assignment,
- ;; go to the start of the assignment instead of the matching item
- (if (or (not lua-indent-close-paren-align)
- (lua-point-is-after-left-shifter-p))
- (current-indentation)
- (current-column)))))))
(defun lua-calculate-indentation ()
"Return appropriate indentation for current line as Lua code."
diff --git a/test/indentation-tests/README.md b/test/indentation-tests/README.md
new file mode 100644
index 0000000..0ae4edc
--- /dev/null
+++ b/test/indentation-tests/README.md
@@ -0,0 +1,63 @@
+## Indentation Tests
+
+This directory contains indentation tests for `lua-mode`.
+
+Each `*.lua` file will be processed by `test-indentation.el` as follows:
+
+- the file itself will be added as a buttercup `describe` block
+
+- each span of code between comments will be added as `it` block that checks
that the code is reindented as given in the file
+
+- last line of comment before Lua code span will be used as the name of `it`
block
+
+- spans of code that have empty name will be called `section 1`, `section 2`
and so on
+
+- spans of code that contain only whitespace will be skipped
+
+- if the name of the code span contains `XFAIL`, the test will be created as
an expected failure (`xit` in buttercup)
+
+### Example
+
+Here's an example:
+
+```lua
+
+-- function call indentation
+
+function(
+ 1,
+ 2,
+ 3,
+)
+
+-- XXX: this is commented out for now
+-- while block indentation
+--
+-- while true do
+-- print(123)
+-- end
+--
+-- function literal indentation
+
+local x = function()
+ return 1, 2, 3
+end
+
+```
+
+It will create two tests, "function call indentation" and "function literal"
indentation. The test called "while block indentation" will be ignored
completely.
+
+### Adding Configuration Parameters
+
+To add configuration parameters use Emacs syntax for Local Variables:
+
+```
+
+-- Local Variables:
+-- lua-indent-close-paren-align: nil
+-- lua-indent-only-use-last-opener: t
+-- End:
+
+```
+
+This can go anywhere in the file, but make sure that the code span after the
local variables section has a name comment, otherwise it will use `End:` line
as a name.
diff --git a/test/indentation-tests/assignment-indentation.lua
b/test/indentation-tests/assignment-indentation.lua
new file mode 100644
index 0000000..10adb60
--- /dev/null
+++ b/test/indentation-tests/assignment-indentation.lua
@@ -0,0 +1,188 @@
+-- ensure is sane
+
+foo = 10
+
+bar = 20
+
+-- add continuation before =
+
+foo
+ = 10
+
+bar = 20
+
+-- add continuation after =
+
+foo =
+ 10
+bar = 20
+
+-- continuation after comma: 1
+foo,
+ baz = 10, 20
+
+bar = 20
+
+-- continuation after comma: 2
+
+foo, baz
+ = 10, 20
+
+bar = 20
+
+-- continuation after comma: 3
+
+foo, baz = 10,
+ 20
+
+bar = 20
+
+-- continuation after comma: 4
+
+foo,
+ baz =
+ 10, 20
+
+-- continuation after comma: 5
+
+foo, baz =
+ 10,
+ 20
+
+bar = 20
+
+-- continuation after "local": 1
+
+local
+ x = 5
+
+-- continuation after "local": 2
+
+local
+ x,
+ y = 10, 20
+
+-- continuation after "local": 3
+
+local
+ x,
+ y =
+ 10,
+ 20
+
+-- continuation after "local": 4
+
+local
+ x = 5
+
+-- indentation of function call arguments in continuation part
+
+x = foo(123,
+ 456)
+ + bar(
+ qux,
+ quux)
+
+-- does not indent binary operators inside parentheses: alignment 1
+
+x = (very_very_very_long_name() +
+ another_very_very_very_long_name())
+
+-- does not indent binary operators inside parentheses: alignment 2
+
+x = (very_very_very_long_name()
+ + another_very_very_very_long_name())
+
+-- does not indent binary operators inside parentheses: indentation 1
+
+x = (
+ very_very_very_long_name() +
+ another_very_very_very_long_name()
+)
+
+-- does not indent binary operators inside parentheses: indentation 2
+
+x = (
+ very_very_very_long_name()
+ + another_very_very_very_long_name()
+)
+
+-- it unindents close paren for arithmetical expression
+
+a = (
+ foo +
+ bar
+)
+
+-- it unindents close paren for arithmetical expression: local
+
+local a = (
+ foo +
+ bar
+)
+
+-- it unindents close paren for function call
+
+a = myfunc(
+ foo +
+ bar
+)
+
+-- it unindents close paren for function call: local
+
+local a = myfunc(
+ foo +
+ bar
+)
+
+-- it unindents close brace for table ctor
+
+a = {
+ foo,
+ bar
+}
+
+-- it unindents close brace for table ctor: local
+
+local a = {
+ foo,
+ bar
+}
+
+-- XFAIL: it unindents close bracket for indexing
+
+a = myobj[
+ foo +
+ bar
+]
+
+-- XFAIL: it unindents close bracket for indexing: local
+
+local a = myobj[
+ foo +
+ bar
+]
+
+-- does not indent binary operators inside brackets: alignment 1
+
+x = t[very_very_very_long_name() +
+ another_very_very_very_long_name()]
+
+-- does not indent binary operators inside brackets: alignment 2
+
+x = t[very_very_very_long_name()
+ + another_very_very_very_long_name()]
+
+-- does not indent binary operators inside brackets: indentation 1
+
+x = [
+ very_very_very_long_name() +
+ another_very_very_very_long_name()
+ ]
+
+-- does not indent binary operators inside brackets: indentation 2
+
+x = [
+ very_very_very_long_name()
+ + another_very_very_very_long_name()
+ ]
diff --git a/test/indentation-tests/continuation-lines.lua
b/test/indentation-tests/continuation-lines.lua
new file mode 100644
index 0000000..b3aa6d5
--- /dev/null
+++ b/test/indentation-tests/continuation-lines.lua
@@ -0,0 +1,181 @@
+-- indentation if broken in the middle of \"foo.bar\" and \"qux:quux\"
+foo123
+ .bar:baz(xyz)
+
+foo123.
+ bar:baz(xyz)
+
+foo123.bar
+ :baz(xyz)
+
+foo123.bar:
+ baz(xyz)
+
+foo123.bar
+ .baz
+ .qux
+ :quux(xyz)
+
+-- indentation after return
+
+function foo()
+ return
+ 123
+end
+
+-- indentation after return: blocks
+
+do
+ return
+ 123
+end
+
+do
+ return
+ x +
+ y
+end
+
+do
+ return
+end
+
+foo = bar
+
+-- indentation after return: f1
+
+function f1()
+ if foo == bar then
+ return
+ else
+ foo = bar
+ end
+end
+
+-- indentation after return: f2
+
+function f2()
+ if foo == bar then
+ return
+ elseif foo != bar then
+ foo = bar
+ end
+end
+
+-- indentation after return: f3
+
+function f3()
+ repeat
+ return
+ until foo == bar
+end
+
+-- indentation after ellipsis
+
+function x(...)
+ a, b = 1, ...
+ return b
+end
+
+
+-- indentation in block-intros: while
+
+while
+ foo do
+ a = a + 1
+end
+
+a = 0
+
+-- indentation in block-intros: while 2
+
+while
+ foo
+do
+ a = a + 1
+end
+
+a = 0
+
+-- indents expressions after return: basic
+
+function myfunc()
+ return
+ 123
+end
+
+-- indents expressions after return: function literal
+
+function myfunc()
+ return
+ function() return 123 end
+end
+
+-- indents expressions after return: ellipsis
+
+function myfunc(...)
+ return
+ ...
+end
+
+-- does not indents keywords after return: end
+
+function myfunc()
+ return
+end
+
+-- does not indents keywords after return: if/end
+
+function myfunc()
+ if true then
+ return
+ end
+end
+
+-- does not indents keywords after return: if/else
+
+function myfunc()
+ if true then
+ return
+ else
+ print(123)
+ end
+end
+
+-- does not indents keywords after return: if/elseif
+
+function myfunc()
+ if true then
+ return
+ elseif false then
+ print(123)
+ end
+end
+
+-- does not indents keywords after return: repeat/until
+
+function myfunc()
+ repeat
+ return
+ until true
+end
+
+-- does not indents keywords after return: semicolon 1
+
+function myfunc()
+ if true then
+ return;
+ end
+end
+
+-- does not indents keywords after return: semicolon 2
+
+function myfunc()
+ if true then
+ return;
+ hello_world() -- this is incorrect syntax, but it's fine
+ else
+ return
+ hello_world()
+ end
+end
diff --git a/test/indentation-tests/do-block.lua
b/test/indentation-tests/do-block.lua
new file mode 100644
index 0000000..9d237e2
--- /dev/null
+++ b/test/indentation-tests/do-block.lua
@@ -0,0 +1,30 @@
+-- works for do ... end blocks on separate lines
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for do ... end blocks: single line
+
+do a = a + 1 end
+
+a = 0
+
+-- works for do ... end blocks: body on the same line
+do a = a + 1
+end
+
+a = 0
+
+-- works for do ... end blocks: continuation inside body
+do a = a
+ + 1 end
+
+a = 0
+
+-- works for do ... end blocks: parentheses inside body
+do a = (a
+ + 1) end
+
+a = 0
diff --git a/test/indentation-tests/for-equals-block.lua
b/test/indentation-tests/for-equals-block.lua
new file mode 100644
index 0000000..6609560
--- /dev/null
+++ b/test/indentation-tests/for-equals-block.lua
@@ -0,0 +1,66 @@
+-- works for "for ... = ... do" block: 1
+for y = 0, 10 do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 2
+for y = 0, 10
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 3
+for y = 0,
+ 10 do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 4
+for y = 0,
+ 10
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 5
+for y =
+ 0, 10 do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 6
+for y =
+ 0, 10
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: 7
+for y = foo(
+ 1,
+ 2),
+ bar(3,
+ 4)
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for ... = ... do" block: single line
+
+for y = 0, 10 do a = a + 1 end
+
+a = 0
diff --git a/test/indentation-tests/for-in-block.lua
b/test/indentation-tests/for-in-block.lua
new file mode 100644
index 0000000..c615dce
--- /dev/null
+++ b/test/indentation-tests/for-in-block.lua
@@ -0,0 +1,86 @@
+-- works for "for .. in .. do" block: 1
+
+for k, v in pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 2
+
+for
+ k, v in pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 3
+
+for
+ k,
+ v in pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 4
+
+for
+ k,
+ v in
+ pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 5
+
+for k, v in
+ pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 6
+
+for k, v in
+ pairs(bar)
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 7
+
+for k, v
+ in pairs(bar) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: 8
+
+for k, v
+ in pairs(bar) do a = a + 1 end
+
+a = 0
+
+
+-- works for "for .. in .. do" block: 9
+for k, v in pairs(bar)
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for "for .. in .. do" block: single line
+for k, v in pairs(bar) do a = a + 1 end
+
+a = 0
diff --git a/test/indentation-tests/goto-label.lua
b/test/indentation-tests/goto-label.lua
new file mode 100644
index 0000000..2920ea9
--- /dev/null
+++ b/test/indentation-tests/goto-label.lua
@@ -0,0 +1,126 @@
+-- is sane
+::foo::
+::bar::
+
+::baz::
+
+a = 0
+
+-- does not affect indentation when put on a separate line
+
+for z=1,10 do
+ ::foo::
+ bar
+end
+
+a = 0
+
+-- XFAIL: does not affect indentation before block modifiers
+
+::foo:: for z=1,10 do
+ bar
+end
+
+a = 0
+
+-- does not affect indentation after block modifiers
+
+for z=1,10 do ::foo::
+ bar
+end
+
+a = 0
+
+-- reindents according to luawiki examples: 1
+
+for z=1,10 do
+ ::foo::
+ for y=1,10 do ::bar
+ for x=1,10 do
+ if x^2 + y^2 == z^2 then
+ print('found a Pythagorean triple:', x, y, z)
+ goto done
+ goto done2
+ end
+ end
+ ::done2::
+ end
+end
+
+::done::
+
+-- reindents according to luawiki examples: 2
+for z=1,10 do
+ for y=1,10 do
+ for x=1,10 do
+ if x^2 + y^2 == z^2 then
+ print('found a Pythagorean triple:', x, y, z)
+ print('now trying next z...')
+ goto zcontinue
+ end
+ end
+ end
+ ::zcontinue::
+end
+
+-- reindents according to luawiki examples: 3
+
+for x=1,5 do ::redo::
+ print(x .. ' + 1 = ?')
+ local y = tonumber(io.read'*l')
+ if y ~= x + 1 then goto redo end
+end
+
+-- reindents according to luawiki examples: 4
+
+::a::
+print 'A'
+if math.random() < 0.3 then goto c end
+::b::
+print 'B'
+if math.random() < 0.5 then goto a end
+::c::
+print 'C'
+if math.random() < 0.1 then goto a else goto b end
+
+-- reindents according to luawiki examples: 5
+
+function fact_(n, ans)
+ ::call::
+ if n == 0 then
+ return ans
+ else
+ n, ans = n - 1, ans * n
+ goto call
+ end
+end
+print(fact_(5, 1)) --> 120
+
+-- reindents according to luawiki examples: 6
+
+function f()
+ if not g() then goto fail end
+ if not h() then goto cleanup_g end
+ if not i() then goto cleanup_h end
+ do return true end -- need do/end?
+
+ ::cleanup_h::
+ undo_h()
+ ::cleanup_g::
+ undo_g()
+ ::fail::
+ return false
+end
+
+-- reindents according to luawiki examples: 7
+
+::redo::
+for x=1,10 do
+ for y=1,10 do
+ if not f(x,y) then goto continue end
+ if not g(x,y) then goto skip end
+ if not h(x,y) then goto redo end
+ ::continue::
+ end
+end ::skip::
+print('foo')
diff --git a/test/indentation-tests/if-elseif-else-block.lua
b/test/indentation-tests/if-elseif-else-block.lua
new file mode 100644
index 0000000..ef94cab
--- /dev/null
+++ b/test/indentation-tests/if-elseif-else-block.lua
@@ -0,0 +1,240 @@
+-- works for if/then block: 1
+
+if foo > bar then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: 2
+
+if
+ foo > bar then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: 3
+
+if foo >
+ bar then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: 4
+
+if foo > bar
+then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: 5
+
+if
+ foo > bar
+then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: 6
+
+if foo >
+ bar
+then
+ a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: single line 1
+
+if foo then a = a + 1 end
+
+a = 0
+
+-- works for if/then block: single line 2
+
+if foo
+then a = a + 1 end
+
+a = 0
+
+-- works for if/then block: single line 3
+
+if foo
+then a = a + 1
+end
+
+a = 0
+
+-- works for if/then block: single line 4
+
+if foo
+then a = a
+ + 1
+end
+
+a = 0
+
+-- works for if/else block: 1
+
+if foo then
+ a = a + 1
+else
+ a = a + 2
+end
+
+a = 0
+
+-- works for if/else block: 2
+
+if foo then a = a + 1 else
+ a = a + 2
+end
+
+a = 0
+
+-- works for if/else block: 3
+
+if foo then a = a + 1
+else
+ a = a + 2
+end
+
+a = 0
+
+-- works for if/else block: 4
+
+if foo then
+ a = a + 1
+else a = a + 2 end
+
+a = 0
+
+-- works for if/else block: 5
+
+if foo then
+ a = a + 1
+else a = a + 2
+end
+
+a = 0
+
+
+-- works for if/else block: single line 1
+
+if foo + bar then a = a + 1 else a = a + 2 end
+
+a = 0
+
+-- works for if/else block: single line 2
+
+if foo + bar
+then a = a + 1
+else a = a + 2
+end
+
+a = 0
+
+-- works for if/else block: single line 3
+
+if foo + bar then a = a + 1
+else a = a + 2
+end
+
+a = 0
+
+-- works for if/else block: single line 4
+
+if foo + bar
+then a = a + 1 else a = a + 2
+end
+
+a = 0
+
+-- XFAIL: works for if/else block: single line 5
+
+if foo + bar
+then a = a + 1 else a =
+ a + 2 -- this line should be indented by 2 levels: else+continuation
+end
+
+a = 0
+
+-- works for if/else block: single line 6
+
+if foo
+ + bar
+then a =
+ a + 1
+else a =
+ a + 2
+end
+
+a = 0
+
+
+-- XFAIL: works for if/else block: parentheses in conditional
+
+if (foo
+ + bar) then a = a + 1 else
+ a = a + 2
+end
+
+a = 0
+
+
+-- works for if/elseif/else block: 1
+
+if foo then
+ a = a + 1
+elseif bar then
+ a = a + 2
+elseif baz then
+ a = a + 3
+end
+
+a = 0
+
+-- works for if/elseif/else block: 2
+
+if foo then a = a + 1 elseif bar then
+ a = a + 2
+elseif baz then
+ a = a + 3
+else
+ a = a + 4
+end
+
+a = 0
+
+-- XFAIL: works for if/elseif/else block: 3
+
+if foo then
+ a = a + 1
+elseif bar then a = a + 2 elseif baz then
+ a = a + 3
+else
+ a = a + 4
+end
+
+a = 0
+
+-- XFAIL: works for if/elseif/else block: 4
+
+if foo then
+ a = a + 1
+elseif bar then
+ a = a + 2
+elseif baz then a = a + 3 else
+ a = a + 4
+end
+
+a = 0
diff --git a/test/indentation-tests/issue-33.lua
b/test/indentation-tests/issue-33.lua
new file mode 100644
index 0000000..72efbfa
--- /dev/null
+++ b/test/indentation-tests/issue-33.lua
@@ -0,0 +1,41 @@
+-- don't accumulate indentation after the expression
+a =
+ {
+ }
+
+b =
+ {
+ }
+
+a = {
+ table_elt_indented
+}
+
+a = a +
+ 5 +
+ 10
+
+this_should_be_unindented()
+
+-- here foobar should be indented as simple continuation statement
+a = a +
+ dosmth(
+ ) +
+ foobar
+
+a =
+ do_smth(
+ do_smth_arg
+ )
+
+b =
+ {
+ table_elt0_indented,
+ table_elt1_indented
+ }
+
+this_should_be_unindented_too =
+ {
+ }
+
+this_should_be_unindented_three = etc
diff --git a/test/indentation-tests/only-use-last-opener.lua
b/test/indentation-tests/only-use-last-opener.lua
new file mode 100644
index 0000000..e89413b
--- /dev/null
+++ b/test/indentation-tests/only-use-last-opener.lua
@@ -0,0 +1,97 @@
+-- Local Variables:
+-- lua-indent-close-paren-align: nil
+-- lua-indent-only-use-last-opener: t
+-- End:
+
+-- XFAIL: one param, nested table on same line as opener
+
+foobar({
+ a, b, c
+})
+
+-- XFAIL: two params, nested table on same line as opener
+
+foobar(a, {
+ b,
+ c
+})
+
+foobar({}, {
+ b,
+ c
+})
+
+-- XFAIL: two aligned params, nested table on next line
+
+foobar({},
+ {1, 2, 3})
+
+-- XFAIL: two aligned table params, first has nested tables
+
+foobar({{},
+ {1, 2, 3}},
+ {
+ 4,5,6
+ })
+
+foobar({{},
+ {1, 2, 3}},
+ {
+ 4,5,6
+ }
+)
+
+-- XFAIL: one nested table containing another table
+
+foobar({
+ {4, 5, 6}
+})
+
+-- XFAIL: nested table with indentation: nested table on separate line
+
+foobar(
+ a,
+ {
+ b,
+ c
+ })
+
+foobar(
+ a,
+ {
+ b,
+ c
+ }
+)
+
+-- XFAIL: nested table with alignment: nested table on separate line
+
+foobar(a,
+ {
+ b,
+ c
+ })
+
+foobar(a,
+ {
+ b,
+ c
+ }
+)
+
+-- nested table with indentation: params after nested table
+
+foobar(
+ {
+ a,
+ b
+ },
+ c, d)
+
+foobar(
+ {
+ a,
+ b
+ },
+ c, d
+)
diff --git a/test/indentation-tests/repeat-until-block.lua
b/test/indentation-tests/repeat-until-block.lua
new file mode 100644
index 0000000..5b0bfed
--- /dev/null
+++ b/test/indentation-tests/repeat-until-block.lua
@@ -0,0 +1,57 @@
+-- works for repeat ... until blocks: 1
+
+repeat
+ a = a + 1
+until foo + bar
+
+a = 0
+
+-- works for repeat ... until blocks: 2
+
+repeat
+ a = a + 1
+until
+ foo
+
+a = 0
+
+-- works for repeat ... until blocks: 3
+
+repeat
+ a = a + 1
+until
+ not
+ foo
+
+a = 0
+
+-- works for repeat ... until blocks: 4
+
+repeat
+ a =
+ a + 1
+until
+ not
+ foo
+
+a = 0
+
+-- works for repeat ... until blocks: single line
+
+repeat a = a + 1 until not foo
+
+a = 0
+
+-- works for repeat ... until blocks: single line with continuation 1
+
+repeat a = a + 1 until
+ not foo
+
+a = 0
+
+-- XFAIL: works for repeat ... until blocks: single line with continuation 1
+
+repeat a =
+ a + 1 until not foo
+
+a = 0
diff --git a/test/indentation-tests/smoke.lua b/test/indentation-tests/smoke.lua
new file mode 100644
index 0000000..d83a8d7
--- /dev/null
+++ b/test/indentation-tests/smoke.lua
@@ -0,0 +1,3 @@
+function hello()
+ print("Hello, world")
+end
diff --git a/test/indentation-tests/while-block.lua
b/test/indentation-tests/while-block.lua
new file mode 100644
index 0000000..86ff45d
--- /dev/null
+++ b/test/indentation-tests/while-block.lua
@@ -0,0 +1,95 @@
+-- works for while ... do ... end blocks: 1
+
+while foo + bar do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 2
+
+while foo + bar
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 3
+
+while
+ foo + bar do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 4
+
+while foo +
+ bar do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 5
+
+while
+ foo
+ + bar
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 6
+
+while (
+ foo) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 7
+
+while (
+ foo
+) do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: 8
+
+while (
+ foo
+)
+do
+ a = a + 1
+end
+
+a = 0
+
+-- works for while ... do ... end blocks: single line
+
+while foo + bar do a = a + 1 end
+
+a = 0
+
+-- works for while ... do ... end blocks: single line with continuation in body
+
+while foo + bar do a = a +
+ 1 end
+
+a = 0
+
+-- works for while ... do ... end blocks: single line with parentheses in body
+
+while foo + bar do a = (a +
+ 1) end
+
+a = 0
diff --git a/test/test-indentation.el b/test/test-indentation.el
index 6c7c59d..7cc6d12 100644
--- a/test/test-indentation.el
+++ b/test/test-indentation.el
@@ -1,182 +1,64 @@
;; -*- flycheck-disabled-checkers: (emacs-lisp-checkdoc) -*-
+;; -*- lexical-binding: t -*-
(load (concat (file-name-directory (or load-file-name (buffer-file-name)
default-directory))
"utils.el") nil 'nomessage 'nosuffix)
-(describe "Assignment indentation"
- (it "is sane"
- (lua--reindent-like "\
-foo = 10
-
-bar = 20"))
- (it "adds continuation before ="
- (lua--reindent-like "\
-foo
- = 10
-
-bar = 20"))
-
- (it "adds continuation after ="
- (lua--reindent-like "\
-foo =
- 10
-bar = 20"))
-
-
- ;; (ert-deftest lua-indentation-assignment-with-commas ()
- ;; (lua--reindent-like "\
- ;; foo,
- ;; baz = 10, 20
-
- ;; bar = 20")
- ;; (lua--reindent-like "\
- ;; foo, baz
- ;; = 10, 20
-
- ;; bar = 20")
-
- ;; (lua--reindent-like "\
- ;; foo, baz = 10,
- ;; 20
-
- ;; bar = 20")
-
- ;; (lua--reindent-like "\
- ;; foo,
- ;; baz =
- ;; 10, 20")
-
- ;; (lua--reindent-like "\
- ;; foo, baz =
- ;; 10,
- ;; 20
-
- ;; bar = 20")
-
- ;; (lua--reindent-like "\
- ;; local
- ;; x = 5")
-
- ;; (lua--reindent-like "\
- ;; local
- ;; x,
- ;; y = 10, 20")
-
- ;; (lua--reindent-like "\
- ;; local
- ;; x,
- ;; y =
- ;; 10,
- ;; 20"))
-
- (it "does not accumulate indentation after the expression (issue #33)"
- (lua--reindent-like "\
-a =
- {
- }
-
-b =
- {
- },
-
-
-a = {
- table_elt_indented
-}
-
-a = a +
- 5 +
- 10
-
-this_should_be_unindented()
-
--- here foobar should be indented as simple continuation statement
-a = a +
- dosmth(
- ) +
- foobar
-
-a =
- do_smth(
- do_smth_arg
- )
-
-b =
- {
- table_elt0_indented,
- table_elt1_indented
- }
-
-this_should_be_unindented_too =
- {
- }
-
-this_should_be_unindented_three = etc")))
-
+(require 'buttercup)
+(require 'cl-lib)
+
+
+(defun lua--get-indentation-test-sections (file-path)
+ (with-temp-buffer
+ (insert-file-contents-literally file-path)
+ (hack-local-variables)
+ (let (results
+ section-name
+ (begin (point-min))
+ end
+ cur-str
+ (next-section-name "start"))
+ (goto-char (point-min))
+ (while next-section-name
+ ;; Scan towards the next comment or end of file, save the comment as
+ ;; the name for the section that comes AFTER the current one.
+ (setq next-section-name
+ (when (re-search-forward "^--\\(.*\\)" nil 'noerror)
(lua--string-trim (match-string-no-properties 1))))
+ ;; Record current section bounds and contents
+ (setq end (if next-section-name (match-beginning 0) (point-max)))
+ (setq cur-str (lua--string-trim (buffer-substring-no-properties begin
end)))
+ ;; Save current section to be returned
+ (if (> (length cur-str) 0)
+ (push (list (or section-name (format "section %d" (1+ (length
results))))
+ cur-str
+ file-local-variables-alist)
+ results))
+ ;; Transition to the next iteration of the loop.
+ (setq section-name next-section-name)
+ (setq begin (point)))
+ (nreverse results))))
+
+(defun lua--indentation-test-make-it-or-xit-clause (x)
+ (let ((it-or-xit (if (string-match "XFAIL" (car x)) 'xit 'it)))
+ (eval `(,it-or-xit ,(format "%s" (car x))
+ (let ((lua-code ,(cadr x))
+ ,@(mapcar (lambda (alist-cons)
+ (list (car alist-cons) (cdr
alist-cons)))
+ ;; cl-caddr here is to support Emacs<26
that don't have caddr.
+ (cl-caddr x)))
+ (expect lua-code :to-be-reindented-the-same-way))))))
+
+(let* ((current-path (or load-file-name (buffer-file-name) default-directory))
+ (indentation-tests-dir (concat (file-name-directory current-path)
"indentation-tests"))
+ (indentation-tests (directory-files indentation-tests-dir nil
".*\.lua$" 'nosort)))
+ (mapcar (lambda (test-file)
+ (let ((file-path (expand-file-name test-file
indentation-tests-dir)))
+ (describe (format "Indentation test `%s'" test-file)
+ (mapcar #'lua--indentation-test-make-it-or-xit-clause
+ (lua--get-indentation-test-sections file-path)))))
+ indentation-tests))
(describe "Continuation lines"
- (it "are indented if broken in the middle of \"foo.bar\" and \"qux:quux\""
- (lua--reindent-like "\
-foo123
- .bar:baz(xyz)")
- (lua--reindent-like "\
-foo123.
- bar:baz(xyz)")
- (lua--reindent-like "\
-foo123.bar
- :baz(xyz)")
- (lua--reindent-like "\
-foo123.bar:
- baz(xyz)")
- (lua--reindent-like "\
-foo123.bar
- .baz
- .qux
- :quux(xyz)"))
-
- (it "are indented after return"
- (expect (lua--reindent-like "\
-return
- 123"))
- (expect (lua--reindent-like "\
-do
- return
- 123
-end"))
- (expect (lua--reindent-like "\
-do
- return
- x +
- y
-end")))
-
- (it "are not indented if \"return\" returns no values"
- (expect (lua--reindent-like "\
-do
- return
-end
-
-foo = bar"))
-
- (expect (lua--reindent-like "\
-if foo == bar then
- return
-else
- foo = bar
-end"))
-
- (expect (lua--reindent-like "\
-if foo == bar then
- return
-elseif foo != bar then
- foo = bar
-end"))
-
- (expect (lua--reindent-like "\
-repeat
- return
-until foo == bar")))
-
(it "are indented before/after binary operators"
(let ((binops '("+" "-" "*" "/" "^" "%" ".."
"<" "<=" ">" ">=" "==" "~="
@@ -190,12 +72,6 @@ a = foo
BINOP bar" 'fixedcase)))))
- (it "are not indented after ellipsis"
- (lua--reindent-like "\
-function x(...)
- a, b = 1, ...
- return b
-end"))
(xit "are indented before/after unary operators"
(expect (lua--reindent-like "\
@@ -224,214 +100,9 @@ x = {qux, xyzzy
(expect (lua--reindent-like (replace-regexp-in-string "<>" unop "\
x = {
<>quux
-}")))))
-
- (it "indents function call arguments in continuation part"
- (expect (lua--reindent-like "\
-x = foo(123,
- 456)
- + bar(
- qux,
- quux)")))
-
- (xit "are indented in block-intros"
- (expect (lua--reindent-like "\
-while
- foo do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for k, v
- in pairs(bar) do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for k, v
- in pairs(bar) do a = a + 1 end
-
-a = 0"))))
-
-
-(describe "Block indentation"
- (it "works for do ... end blocks"
- ;; FIXME: test split block-intro indentations
- (expect (lua--reindent-like "\
-do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-do a = a + 1 end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-do a = a + 1
-end
-
-a = 0")))
-
-
- (it "works for while ... do ... end blocks"
- (expect (lua--reindent-like "\
-while foo do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-while foo
-do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-while
- foo
-do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-while foo do a = a + 1 end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-while
- x +
- y > 0
-do
- a = a + 1
-end
-
-a = 0")))
-
-
- (it "works for repeat ... until blocks"
- (expect (lua--reindent-like "\
-repeat
- a = a + 1
-until foo
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-repeat
- a = a + 1
-until
- foo
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-repeat
- a = a + 1
-until
- not
- foo
+}"))))))
-a = 0"))
- (expect (lua--reindent-like "\
-repeat a = a + 1 until not foo
-
-a = 0")))
-
-
-
- (it "works for \"for ... do\" block "
- (expect (lua--reindent-like "\
-for k, v in pairs(bar) do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for k, v in pairs(bar)
-do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for k, v in pairs(bar) do a = a + 1 end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for y = 0, 10 do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for y = 0, 10
-do
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-for y = 0, 10 do a = a + 1 end
-
-a = 0")))
-
- (it "works for conditionals"
- (expect (lua--reindent-like "\
-if foo then
- a = a + 1
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-if foo then a = a + 1 end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-if foo then
- a = a + 1
-else
- a = a + 2
-end
-
-a = 0"))
-
-
- (expect (lua--reindent-like "\
-if foo then
- a = a + 1
-elseif bar then
- a = a + 2
-elseif baz then
- a = a + 3
-end
-
-a = 0"))
-
- (expect (lua--reindent-like "\
-if foo then a = a + 1 else
- a = a + 2
-end"))))
(describe "Function indentation"
(it "indents function call arguments"
@@ -458,59 +129,14 @@ foobar{
a, b, c
}")))
-
- (xit "indents nested tables"
- (expect (lua--reindent-like "\
-foobar({
- a, b, c
-})"))
-
- (expect (lua--reindent-like "\
-foobar(a, {
- b,
- c
-})"))
-
- (expect (lua--reindent-like "\
-foobar(
- a,
- {
- b,
- c
- })"))
-
- (expect (lua--reindent-like "\
-foobar(a,
- {
- b,
- c
- })"))
-
- (expect (lua--reindent-like "\
-foobar(a,
- {
- b,
- c
- }
-)"))
-
- (expect (lua--reindent-like "\
-foobar(
- {
- a,
- b
- },
- c, d
-)")))
-
(it "indent blocks with lua-indent-nested-block-content-align"
- (let ((lua-indent-nested-block-content-align nil))
- (expect (lua--reindent-like "\
+ (let ((lua-indent-nested-block-content-align nil))
+ (expect (lua--reindent-like "\
call_some_fn( something, {
val = 5,
another = 6,
} )"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
local def = {
some_very_long_name = { fn =
function()
@@ -518,34 +144,34 @@ local def = {
end
}
}"))
- ))
+ ))
(it "indent blocks with lua-indent-close-paren-align"
- (let ((lua-indent-close-paren-align nil))
- (expect (lua--reindent-like "\
+ (let ((lua-indent-close-paren-align nil))
+ (expect (lua--reindent-like "\
local foo = setmetatable( {
a = 4,
b = 5,
}, {
__index = some_func,
} )"))
- ))
+ ))
(it "indents nested tables with alternative block indenting"
- (let ((lua-indent-nested-block-content-align nil)
- (lua-indent-close-paren-align nil))
- (expect (lua--reindent-like "\
+ (let ((lua-indent-nested-block-content-align nil)
+ (lua-indent-close-paren-align nil))
+ (expect (lua--reindent-like "\
foobar({
a, b, c
})"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(a, {
b,
c
})"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(
a,
{
@@ -553,7 +179,7 @@ foobar(
c
})"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(
a,
{
@@ -562,14 +188,14 @@ foobar(
}
)"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(a,
{
b,
c
})"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(a,
{
b,
@@ -577,7 +203,7 @@ foobar(a,
}
)"))
- (expect (lua--reindent-like "\
+ (expect (lua--reindent-like "\
foobar(
{
a,
@@ -585,25 +211,7 @@ foobar(
},
c, d
)"))
- )))
-
-(ert-deftest lua-indentation-defun ()
- ;; [local] function funcname funcbody
- ;; FIXME: add
- )
-
-(ert-deftest lua-indentation-alignment ()
- ;; FIXME: add
- )
-
-(ert-deftest lua-indentation-tablector ()
- ;; FIXME: add
- )
-
-(ert-deftest lua-indentation-continuation-spans-over-empty-lines ()
- ;; FIXME: add
- ;; FIXME: check comment-only lines too
- )
+ )))
(ert-deftest lua-indentation-keywords-with-special-characters ()
@@ -611,136 +219,3 @@ foobar(
do
foobar = _do
end")))
-
-
-(describe "Goto label indentation"
- (it "is sane"
- (expect (lua--reindent-like "\
-::foo::
-::bar::
-
-::baz::
-
-a = 0")))
-
- (it "does not affect indentation when put on a separate line"
- (expect (lua--reindent-like "\
-for z=1,10 do
- ::foo::
- bar
-end
-
-a = 0")))
-
- (xit "does not affect indentation before block modifiers"
- (expect (lua--reindent-like "\
-::foo:: for z=1,10 do
- bar
-end
-
-a = 0")))
-
- (it "does not affect indentation after block modifiers"
- (expect (lua--reindent-like "\
-for z=1,10 do ::foo::
- bar
-end
-
-a = 0")))
-
- (it "reindents according to luawiki examples"
- (expect (lua--reindent-like "\
-for z=1,10 do
- ::foo::
- for y=1,10 do ::bar
- for x=1,10 do
- if x^2 + y^2 == z^2 then
- print('found a Pythagorean triple:', x, y, z)
- goto done
- goto done2
- end
- end
- ::done2::
- end
-end
-
-::done::"))
-
- (expect (lua--reindent-like "\
--- 5.2.0-beta-rc2
-for z=1,10 do
- for y=1,10 do
- for x=1,10 do
- if x^2 + y^2 == z^2 then
- print('found a Pythagorean triple:', x, y, z)
- print('now trying next z...')
- goto zcontinue
- end
- end
- end
- ::zcontinue::
-end"))
-
- (expect (lua--reindent-like "\
--- Lua 5.2.0-beta-rc2
-for x=1,5 do ::redo::
- print(x .. ' + 1 = ?')
- local y = tonumber(io.read'*l')
- if y ~= x + 1 then goto redo end
-end"))
-
- (expect (lua--reindent-like "\
--- 5.2.0-beta-rc1
-::a::
-print 'A'
-if math.random() < 0.3 then goto c end
-::b::
-print 'B'
-if math.random() < 0.5 then goto a end
-::c::
-print 'C'
-if math.random() < 0.1 then goto a else goto b end
-"))
-
- (expect (lua--reindent-like "\
--- 5.2.0-beta-rc2 - factorial with tail recursion simulated with goto's
--- (warning: there's no need to do this)
-function fact_(n, ans)
- ::call::
- if n == 0 then
- return ans
- else
- n, ans = n - 1, ans * n
- goto call
- end
-end
-print(fact_(5, 1)) --> 120"))
-
- (expect (lua--reindent-like "\
--- 5.2.0-beta-rc2
-function f()
- if not g() then goto fail end
- if not h() then goto cleanup_g end
- if not i() then goto cleanup_h end
- do return true end -- need do/end?
-
- ::cleanup_h::
- undo_h()
- ::cleanup_g::
- undo_g()
- ::fail::
- return false
-end"))
-
- (expect (lua--reindent-like "\
--- 5.2.0-beta-rc2
-::redo::
-for x=1,10 do
- for y=1,10 do
- if not f(x,y) then goto continue end
- if not g(x,y) then goto skip end
- if not h(x,y) then goto redo end
- ::continue::
- end
-end ::skip::
-print('foo')"))))
diff --git a/test/utils.el b/test/utils.el
index d641cc8..4192db0 100644
--- a/test/utils.el
+++ b/test/utils.el
@@ -136,16 +136,18 @@ This is a mere typing/reading aid for lua-mode's
font-lock tests."
(lua-kill-process)))))
(defun lua-get-indented-strs (strs)
- (butlast
- (split-string
- (with-lua-buffer
- (let ((inhibit-message t))
- (insert (replace-regexp-in-string "^\\s *" "" (lua-join-lines strs)))
- (font-lock-fontify-buffer)
- (indent-region (point-min) (point-max))
- (buffer-substring-no-properties
- (point-min) (point-max))))
- "\n" nil)))
+ (let ((indent-tabs-mode nil)
+ (font-lock-verbose nil))
+ (butlast
+ (split-string
+ (with-lua-buffer
+ (let ((inhibit-message t))
+ (insert (replace-regexp-in-string "^\\s *" "" (lua-join-lines strs)))
+ (font-lock-fontify-buffer)
+ (indent-region (point-min) (point-max))
+ (buffer-substring-no-properties
+ (point-min) (point-max))))
+ "\n" nil))))
(defun lua-insert-goto-<> (strs)
"Insert sequence of strings and put point in place of \"<>\"."
@@ -166,9 +168,7 @@ This is a mere typing/reading aid for lua-mode's font-lock
tests."
"\n" nil)))
(defun lua--reindent-like (str)
- (let ((strs (split-string str "\n"))
- (indent-tabs-mode nil)
- (font-lock-verbose nil))
+ (let ((strs (split-string str "\n")))
(equal strs (lua-get-indented-strs strs))))
(defun with-point-at-matcher (&rest args)
@@ -228,3 +228,29 @@ This is a mere typing/reading aid for lua-mode's font-lock
tests."
(buttercup-define-matcher :with-point-at (&rest args)
(apply #'with-point-at-matcher `(:lua-code ,(car args) :with-point-at ,@(cdr
args))))
+
+
+(defun lua--string-trim (string &optional trim-left trim-right)
+ ;; Backport of string-trim for Emacs 24 that doesn't have subr-x lib.
+ (let ((sub-start 0) sub-end)
+ (or trim-left (setq trim-left "[ \t\n\r]+"))
+ (or trim-right (setq trim-right "[ \t\n\r]+"))
+ (save-match-data
+ (when (string-match (concat "\\`" trim-left) string)
+ (setq sub-start (match-end 0)))
+ (when (string-match (concat trim-right "\\'") string sub-start)
+ (setq sub-end (match-beginning 0))))
+ (if (or sub-start sub-end)
+ (substring string sub-start sub-end)
+ string)))
+
+
+(buttercup-define-matcher :to-be-reindented-the-same-way (str)
+ (let* ((lines (split-string (funcall str) "\n"))
+ (indented-lines (lua-get-indented-strs lines)))
+ (buttercup--test-expectation (equal lines indented-lines)
+ :expect-match-phrase (format "Indentation check
failed:\n=========\nExpected:\n---------\n%s\n---------\nActual:\n---------\n%s\n========="
+ (lua--string-trim (mapconcat 'identity
lines "\n"))
+ (lua--string-trim (mapconcat 'identity
indented-lines "\n")))
+ :expect-mismatch-phrase (format "Expected `%S' to not be reindented like
that"
+ lines))))
- [nongnu] elpa/lua-mode aca4014 356/468: utils: update to new buttercup matcher definition logic (#138), (continued)
- [nongnu] elpa/lua-mode aca4014 356/468: utils: update to new buttercup matcher definition logic (#138), Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode e8281dc 362/468: Fix a minor bug in lua-kill-process, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 1f04654 379/468: travis: enable testing on osx, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode eb93be5 380/468: Merge pull request #158 from immerrr/enable-testing-on-osx, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 4cdee31 388/468: Use proper lexical-bindings instead of lexical-let*, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 5dfc5be 399/468: Merge pull request #168 from immerrr/index-requires-as-imenu-items, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode a3a71b1 400/468: Merge pull request #151 from edam/indent-nested-blocks, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 35b6e4c 402/468: Merge pull request #169 from tarsiiformes/typos, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 724bfa5 409/468: Fix support for file-local vars in indentation tests, add tests for blocks, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 1365ba0 412/468: Enable XFAIL-ing tests for continuation in block intros, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 51a32ef 417/468: Merge pull request #176 from immerrr/improve-indentation-for-continuation-lines,
Philip Kaludercic <=
- [nongnu] elpa/lua-mode b37710e 418/468: Enable fontification of variable names in "for" and function parameters, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 9c275cd 419/468: Add regression test for issue #157, fix lua-get-line-faces, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 5fe5f57 422/468: Fix wait-for-prompt to take lua-prompt-regexp into account, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 83ec53d 430/468: `lua-funcname-at-point': be more strict, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 70b9384 448/468: Don't evaluate lua-is-continuing-statement-p-1 twice, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 350acce 450/468: Don't do lua-backward-up-list calculation for overrides if not necessary, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode 3e89784 454/468: Fix test suite names, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode af274e4 455/468: Merge pull request #184 from immerrr/fix-and-optimize-finding-matching-blocks, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode d81c700 466/468: Bump version tag, Philip Kaludercic, 2021/08/05
- [nongnu] elpa/lua-mode b9a476c 467/468: Merge pull request #194 from phikal/patch-1, Philip Kaludercic, 2021/08/05