emacs-elpa-diffs
[Top][All Lists]
Advanced

[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))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]