Subject: Re: [PATCH] Various improvements to lua-ts-mode (Bug#66159)

From: john muhl <[email-masked]>
Date: Sun, 08 Oct 2023 22:28:02 -0500

Mauro Aranda <[email-masked]> writes:

> On 6/10/23 16:44, john muhl via Bug reports for GNU Emacs, the Swiss
> army knife of text editors wrote:
>
>> +(defcustom lua-ts-inferior-history nil
>> +  "File used to save command history of the inferior Lua process."
>> +  :type '(choice (const nil) file)
>
> Please, give a :tag for the const option.  IMO, it makes for a better
> Customize UI.

Fixed here and for the other choices.

Andrey Listopadov <[email-masked]> writes:

> john muhl <[email-masked]> writes:
>
>>> It seems the last patch broke the movement.
>>
>> The tests for those pass and are still working here. Maybe you found
>> another case that needs to be improved. What is the specific problem?
>
> It seems so, sorry for not including this, I thought I sent this before.
> Here's an example:
>
>
> █for i=1,10 do
>      print(x)
>  end
>
> Pressing Pressing C-M-f (forward-sexp) puts the point here:
>
>  for i=1,10 do
>      print(x)█
>  end
>
> I think it should go over the `for' loop right to the `end'.
>
> Pressing Pressing C-M-b (backward-sexp), however, puts the point here:
>
>  for i=1,10 do
>     █print(x)
>  end
>
> Pressing C-M-b again doesn't move the point anymore.
>
> Same thing happens with for-each style loop:
>
> █for k,v in pairs({1,2,3}) do
>      print(x)
>  end
>
> C-M-f:
>
>  for k,v in pairs({1,2,3})█do
>      print(x)
>  end
>
> C-M-f:
>
>  for k,v in pairs({1,2,3}) do
>      print(x)█
>  end
>
> Backward movement manages to take the point way back to the pairs:
>
> C-M-b, C-M-b:
>
>  for k,v in pairs█({1,2,3}) do
>      print(x)
>  end
>
> I went to https://devhints.io/lua and copied a bunch of examples of
> other loops to the scratch buffer, and the movement is as follows:
>
> █while condition do
>  end
>
>  for i = 1,5 do
>  end
>
>  for i = start,finish,delta do
>  end
>
>  for k,v in pairs(tab) do
>  end
>
>  repeat
>  until condition
>
>  -- Breaking out:
>  while x do
>    if condition then break end
>  end
>
> C-M-f:
>
>  while condition do
>  end
>
>  for i = 1,5 do
>  end
>
>  for i = start,finish,delta do
>  end
>
>  for k,v in pairs(tab)█do
>  end
>
>  repeat
>  until condition
>
>  -- Breaking out:
>  while x do
>    if condition then break end
>  end
>
> C-M-f:
>
>  while condition do
>  end
>
>  for i = 1,5 do
>  end
>
>  for i = start,finish,delta do
>  end
>
>  for k,v in pairs(tab) do
>  end
>
>  repeat
>  until condition
>
>  -- Breaking out:
>  while x do
>    if condition then break end█
>  end

Navigation should be all around improved now. Let me know if I missed something.

>From de03aed39d67f0a14b2586e59b2b713891bc37d0 Mon Sep 17 00:00:00 2001
From: john muhl <[email-masked]>
Date: Wed, 4 Oct 2023 20:46:15 -0500
Subject: [PATCH] Various improvements to lua-ts-mode (Bug#66159)

* lisp/progmodes/lua-ts-mode.el (lua-ts-mode): Navigate function
declarations and parenthesized expressions with *-sexp commands.
(lua-ts--simple-indent-rules): Improve indentation rules.
(lua-ts-mode-map): Add key bindings and menus.
(lua-ts-mode-hook): Make hook available in Customize.
(lua-ts-inferior-history):
(lua-ts-inferior--write-history): Add option to read/write
an input history file.
(lua-ts-inferior-lua):
(lua-ts-send-buffer):
(lua-ts-send-file):
(lua-ts-send-region):
(lua-ts-inferior-prompt):
(lua-ts-inferior-prompt-continue): Support for sending buffer,
file or region to the inferior process.
(lua-ts-show-process-buffer):
(lua-ts-hide-process-buffer):
(lua-ts-kill-process): New functions.
(lua-ts-inferior-prompt-regexp): Remove option.
* test/lisp/progmodes/lua-ts-mode-resources/indent.erts:
* test/lisp/progmodes/lua-ts-mode-resources/movement.erts: Add
tests.
---
 lisp/progmodes/lua-ts-mode.el                 | 369 +++++++--
 .../lua-ts-mode-resources/indent.erts         | 705 +++++++++++++++---
 .../lua-ts-mode-resources/movement.erts       | 156 ++--
 3 files changed, 1032 insertions(+), 198 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..e7a36f3bb93 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -38,7 +38,11 @@
   (require 'cl-lib)
   (require 'rx))
 
+(declare-function treesit-induce-sparse-tree "treesit.c")
 (declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-first-child-for-pos "treesit.c")
+(declare-function treesit-node-parent "treesit.c")
+(declare-function treesit-node-start "treesit.c")
 (declare-function treesit-node-type "treesit.c")
 (declare-function treesit-parser-create "treesit.c")
 (declare-function treesit-search-subtree "treesit.c")
@@ -48,6 +52,15 @@ lua-ts
   :prefix "lua-ts-"
   :group 'languages)
 
+(defcustom lua-ts-mode-hook nil
+  "Hook run after entering `lua-ts-mode'."
+  :type 'hook
+  :options '(flymake-mode
+             hs-minor-mode
+             outline-minor-mode)
+  :group 'lua-ts
+  :version "30.1")
+
 (defcustom lua-ts-indent-offset 4
   "Number of spaces for each indentation step in `lua-ts-mode'."
   :type 'natnum
@@ -57,7 +70,7 @@ lua-ts-indent-offset
 
 (defcustom lua-ts-luacheck-program "luacheck"
   "Location of the Luacheck program."
-  :type '(choice (const nil) string)
+  :type '(choice (const :tag "None" nil) string)
   :group 'lua-ts
   :version "30.1")
 
@@ -70,7 +83,7 @@ lua-ts-inferior-buffer
 
 (defcustom lua-ts-inferior-program "lua"
   "Program to run in the inferior Lua process."
-  :type '(choice (const nil) string)
+  :type '(choice (const :tag "None" nil) string)
   :group 'lua-ts
   :version "30.1")
 
@@ -82,13 +95,28 @@ lua-ts-inferior-options
 
 (defcustom lua-ts-inferior-startfile nil
   "File to load into the inferior Lua process at startup."
-  :type '(choice (const nil) (file :must-match t))
+  :type '(choice (const :tag "None" nil) (file :must-match t))
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-prompt ">"
+  "Prompt used by the inferior Lua process."
+  :type 'string
+  :safe 'stringp
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-prompt-continue ">>"
+  "Continuation prompt used by the inferior Lua process."
+  :type 'string
+  :safe 'stringp
   :group 'lua-ts
   :version "30.1")
 
-(defcustom lua-ts-inferior-prompt-regexp "^>>?[[:blank:]]"
-  "Regular expression matching the prompt of the inferior Lua process."
-  :type 'regexp
+(defcustom lua-ts-inferior-history nil
+  "File used to save command history of the inferior Lua process."
+  :type '(choice (const :tag "None" nil) file)
+  :safe 'string-or-null-p
   :group 'lua-ts
   :version "30.1")
 
@@ -103,6 +131,12 @@ lua-ts--builtins
     "close" "flush" "lines" "read" "seek" "setvbuf" "write")
   "Lua built-in functions for tree-sitter font-locking.")
 
+(defvar lua-ts--keywords
+  '("and" "do" "else" "elseif" "end" "for" "function"
+    "goto" "if" "in" "local" "not" "or" "repeat"
+    "return" "then" "until" "while")
+  "Lua keywords for tree-sitter font-locking and navigation.")
+
 (defvar lua-ts--font-lock-settings
   (treesit-font-lock-rules
    :language 'lua
@@ -167,13 +201,11 @@ lua-ts--font-lock-settings
 
    :language 'lua
    :feature 'keyword
-   '((break_statement) @font-lock-keyword-face
+   `((break_statement) @font-lock-keyword-face
      (true) @font-lock-constant-face
      (false) @font-lock-constant-face
      (nil) @font-lock-constant-face
-     ["and" "do" "else" "elseif" "end" "for" "function"
-      "goto" "if" "in" "local" "not" "or" "repeat"
-      "return" "then" "until" "while"]
+     ,(vconcat lua-ts--keywords)
      @font-lock-keyword-face)
 
    :language 'lua
@@ -235,27 +267,135 @@ lua-ts--font-lock-settings
 
 (defvar lua-ts--simple-indent-rules
   `((lua
+     ((or (node-is "comment")
+          (parent-is "comment_content")
+          (parent-is "string_content")
+          (node-is "]]"))
+      no-indent 0)
+     ((and (n-p-gp "field" "table_constructor" "arguments")
+           lua-ts--multi-arg-function-call-matcher)
+      parent lua-ts-indent-offset)
+     ((and (n-p-gp "}" "table_constructor" "arguments")
+           lua-ts--multi-arg-function-call-matcher)
+      parent 0)
+     ((or (node-is "do")
+          (node-is "then")
+          (node-is "elseif_statement")
+          (node-is "else_statement")
+          (node-is "until")
+          (node-is ")")
+          (node-is "}"))
+      standalone-parent 0)
+     ((or (and (parent-is "arguments") lua-ts--first-child-matcher)
+          (and (parent-is "parameters") lua-ts--first-child-matcher)
+          (and (parent-is "table_constructor") lua-ts--first-child-matcher))
+      standalone-parent lua-ts-indent-offset)
+     ((or (parent-is "arguments")
+          (parent-is "parameters")
+          (parent-is "table_constructor"))
+      (nth-sibling 1) 0)
+     ((and (n-p-gp "block" "function_definition" "parenthesized_expression")
+           lua-ts--nested-function-block-matcher
+           lua-ts--nested-function-block-include-matcher)
+      parent lua-ts-indent-offset)
+     ((and (n-p-gp "block" "function_definition" "arguments")
+           lua-ts--nested-function-argument-matcher)
+      parent lua-ts-indent-offset)
+     ((match "function_definition" "parenthesized_expression")
+      standalone-parent lua-ts-indent-offset)
+     ((node-is "block") standalone-parent lua-ts-indent-offset)
+     ((parent-is "block") parent 0)
+     ((and (node-is "end") lua-ts--end-line-matcher)
+      standalone-parent lua-ts--end-indent-offset)
+     ((match "end" "function_declaration") parent 0)
+     ((and (n-p-gp "end" "function_definition" "parenthesized_expression")
+           lua-ts--nested-function-end-argument-matcher)
+      parent 0)
+     ((and (n-p-gp "end" "function_definition" "parenthesized_expression")
+           lua-ts--nested-function-block-matcher
+           lua-ts--nested-function-end-matcher
+           lua-ts--nested-function-last-function-matcher)
+      parent 0)
+     ((n-p-gp "end" "function_definition" "arguments") parent 0)
+     ((or (match "end" "function_definition")
+          (node-is "end"))
+      standalone-parent 0)
      ((parent-is "chunk") column-0 0)
-     ((node-is "comment_end") column-0 0)
-     ((parent-is "block") parent-bol 0)
-     ((node-is "}") parent-bol 0)
-     ((node-is ")") parent-bol 0)
-     ((node-is "else_statement") parent-bol 0)
-     ((node-is "elseif_statement") parent-bol 0)
-     ((node-is "end") parent-bol 0)
-     ((node-is "until") parent-bol 0)
-     ((parent-is "for_statement") parent-bol lua-ts-indent-offset)
-     ((parent-is "function_declaration") parent-bol lua-ts-indent-offset)
-     ((parent-is "function_definition") parent-bol lua-ts-indent-offset)
-     ((parent-is "if_statement") parent-bol lua-ts-indent-offset)
-     ((parent-is "else_statement") parent-bol lua-ts-indent-offset)
-     ((parent-is "repeat_statement") parent-bol lua-ts-indent-offset)
-     ((parent-is "while_statement") parent-bol lua-ts-indent-offset)
-     ((parent-is "table_constructor") parent-bol lua-ts-indent-offset)
-     ((parent-is "arguments") parent-bol lua-ts-indent-offset)
-     ((parent-is "parameters") parent-bol lua-ts-indent-offset)
      ((parent-is "ERROR") no-indent 0))))
 
+(defun lua-ts--end-line-matcher (&rest _)
+  "Matches if there is more than one `end' on the current line."
+  (> (lua-ts--end-count) 1))
+
+(defun lua-ts--end-indent-offset (&rest _)
+  "Calculate indent offset based on `end' count."
+  (- (* (1- (lua-ts--end-count)) lua-ts-indent-offset)))
+
+(defun lua-ts--end-count ()
+  "Count the number of `end's on the current line."
+  (count-matches "end" (line-beginning-position) (line-end-position)))
+
+(defun lua-ts--first-child-matcher (node &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
+(defun lua-ts--function-definition-p (node)
+  "Return t if NODE is a function_definition."
+  (equal "function_definition" (treesit-node-type node)))
+
+(defun lua-ts--g-g-g-parent (node)
+  "Return the great-great-grand-parent of NODE."
+  (let* ((parent (treesit-node-parent node))
+         (g-parent (treesit-node-parent parent))
+         (g-g-parent (treesit-node-parent g-parent)))
+    (treesit-node-parent g-g-parent)))
+
+(defun lua-ts--multi-arg-function-call-matcher (_n parent &rest _)
+  "Matches if PARENT has multiple arguments."
+  (> (treesit-node-child-count (treesit-node-parent parent)) 3))
+
+(defun lua-ts--nested-function-argument-matcher (node &rest _)
+  "Matches if NODE is in a nested function argument."
+  (save-excursion
+    (goto-char (treesit-node-start node))
+    (treesit-beginning-of-defun)
+    (backward-char 2)
+    (not (looking-at ")("))))
+
+(defun lua-ts--nested-function-block-matcher (node &rest _)
+  "Matches if NODE is in a nested function block."
+  (let* ((g-g-g-parent (lua-ts--g-g-g-parent node))
+         (g-g-g-type (treesit-node-type g-g-g-parent)))
+    (not (equal g-g-g-type "chunk"))))
+
+(defun lua-ts--nested-function-block-include-matcher (node _p bol &rest _)
+  "Matches if NODE's child at BOL is not another block."
+  (let* ((child (treesit-node-first-child-for-pos node bol))
+         (child-type (treesit-node-type child))
+         (g-g-g-type (treesit-node-type (lua-ts--g-g-g-parent node))))
+    (or (equal child-type "assignment_statement")
+        (and (equal child-type "return_statement")
+             (or (equal g-g-g-type "arguments")
+                 (and (equal g-g-g-type "expression_list")
+                      (not (treesit-search-subtree child "function_call"))))))))
+
+(defun lua-ts--nested-function-end-matcher (node &rest _)
+  "Matches if NODE is the `end' of a nested function."
+  (save-excursion
+    (goto-char (treesit-node-start node))
+    (treesit-beginning-of-defun)
+    (looking-at "function[[:space:]]*")))
+
+(defun lua-ts--nested-function-end-argument-matcher (node &rest _)
+  "Matches if great-great-grandparent of NODE is arguments."
+  (equal "arguments" (treesit-node-type (lua-ts--g-g-g-parent node))))
+
+(defun lua-ts--nested-function-last-function-matcher (_n parent &rest _)
+  "Matches if PARENT is the last nested function."
+  (let ((sparse-tree
+         (treesit-induce-sparse-tree parent #'lua-ts--function-definition-p)))
+    (= 1 (length (cadr sparse-tree)))))
+
 (defvar lua-ts--syntax-table
   (let ((table (make-syntax-table)))
     (modify-syntax-entry ?+  "."    table)
@@ -352,26 +492,124 @@ lua-ts-flymake-luacheck
 (defun lua-ts-inferior-lua ()
   "Run a Lua interpreter in an inferior process."
   (interactive)
-  (let* ((buffer lua-ts-inferior-buffer)
-         (name (string-replace "*" "" buffer))
-         (program lua-ts-inferior-program)
-         (prompt-regexp lua-ts-inferior-prompt-regexp)
-         (switches lua-ts-inferior-options)
-         (startfile lua-ts-inferior-startfile))
-    (unless (comint-check-proc buffer)
-      (set-buffer (apply (function make-comint) name program startfile switches))
+  (unless (comint-check-proc lua-ts-inferior-buffer)
+    (apply #'make-comint-in-buffer
+           (string-replace "*" "" lua-ts-inferior-buffer)
+           lua-ts-inferior-buffer
+           lua-ts-inferior-program
+           lua-ts-inferior-startfile
+           lua-ts-inferior-options)
+    (when lua-ts-inferior-history
+        (set-process-sentinel (get-buffer-process lua-ts-inferior-buffer)
+                              'lua-ts-inferior--write-history))
+    (with-current-buffer lua-ts-inferior-buffer
       (setq-local comint-input-ignoredups t
+                  comint-input-ring-file-name lua-ts-inferior-history
+                  comint-use-prompt-regexp t
                   comint-prompt-read-only t
-                  comint-prompt-regexp prompt-regexp
-                  comint-use-prompt-regexp t))
-    (select-window (display-buffer buffer '((display-buffer-reuse-window
-                                             display-buffer-pop-up-frame)
-                                            (reusable-frames . t))))))
+                  comint-prompt-regexp (rx-to-string `(: bol
+                                                         ,lua-ts-inferior-prompt
+                                                         (1+ space))))
+      (comint-read-input-ring t)
+      (add-hook 'comint-preoutput-filter-functions
+                (lambda (string)
+                  (if (equal string (concat lua-ts-inferior-prompt-continue " "))
+                      string   ; Don't mess with continuation prompts.
+                    (concat
+                     ;; Filter out the extra prompt characters that
+                     ;; accumulate in the output when sending regions
+                     ;; to the inferior process.
+                     (replace-regexp-in-string (rx-to-string
+                                                `(: bol
+                                                    (* ,lua-ts-inferior-prompt
+                                                       (? ,lua-ts-inferior-prompt)
+                                                       (1+ space))
+                                                    (group (* nonl))))
+                                               "\\1" string)
+                     ;; Re-add the prompt for the next line.
+                     lua-ts-inferior-prompt " "))))))
+  (select-window (display-buffer lua-ts-inferior-buffer
+                                 '((display-buffer-reuse-window
+                                    display-buffer-pop-up-frame)
+                                   (reusable-frames . t))))
+  (get-buffer-process (current-buffer)))
+
+(defun lua-ts-send-buffer ()
+  "Send current buffer to the inferior Lua process."
+  (interactive)
+  (lua-ts-send-region (point-min) (point-max)))
+
+(defun lua-ts-send-file (file)
+  "Send contents of FILE to the inferior Lua process."
+  (interactive "f")
+  (with-temp-buffer
+    (insert-file-contents-literally file)
+    (lua-ts-send-region (point-min) (point-max))))
+
+(defun lua-ts-send-region (beg end)
+  "Send region between BEG and END to the inferior Lua process."
+  (interactive "r")
+  (let ((string (buffer-substring-no-properties beg end))
+        (proc-buffer (lua-ts-inferior-lua)))
+    (comint-send-string proc-buffer "print()") ; Prevent output from
+    (comint-send-string proc-buffer "\n")      ; appearing at prompt.
+    (comint-send-string proc-buffer string)
+    (comint-send-string proc-buffer "\n")))
+
+(defun lua-ts-show-process-buffer ()
+  "Show the inferior Lua process buffer."
+  (interactive)
+  (display-buffer lua-ts-inferior-buffer))
+
+(defun lua-ts-hide-process-buffer ()
+  "Hide the inferior Lua process buffer."
+  (interactive)
+  (delete-windows-on lua-ts-inferior-buffer))
+
+(defun lua-ts-kill-process ()
+  "Kill the inferior Lua process."
+  (interactive)
+  (with-current-buffer lua-ts-inferior-buffer
+    (kill-buffer-and-window)))
+
+(defun lua-ts-inferior--write-history (process _)
+  "Write history file for inferior Lua PROCESS."
+  ;; Depending on how the process is killed the buffer may not be
+  ;; around anymore; e.g. `kill-buffer'.
+  (when-let* ((buffer (process-buffer process))
+              ((buffer-live-p (process-buffer process))))
+    (with-current-buffer buffer (comint-write-input-ring))))
+
+(defvar lua-ts-mode-map
+  (let ((map (make-sparse-keymap "Lua")))
+    (define-key map "\C-c\C-n" 'lua-ts-inferior-lua)
+    (define-key map "\C-c\C-c" 'lua-ts-send-buffer)
+    (define-key map "\C-c\C-l" 'lua-ts-send-file)
+    (define-key map "\C-c\C-r" 'lua-ts-send-region)
+    map)
+  "Keymap for `lua-ts-mode' buffers.")
+
+(easy-menu-define lua-ts-mode-menu lua-ts-mode-map
+  "Menu bar entry for `lua-ts-mode'."
+  `("Lua"
+    ["Evaluate Buffer" lua-ts-send-buffer]
+    ["Evaluate File" lua-ts-send-file]
+    ["Evaluate Region" lua-ts-send-region]
+    "--"
+    ["Start Process" lua-ts-inferior-lua]
+    ["Show Process Buffer" lua-ts-show-process-buffer]
+    ["Hide Process Buffer" lua-ts-hide-process-buffer]
+    ["Kill Process" lua-ts-kill-process]
+    "--"
+    ["Customize" (lambda () (interactive) (customize-group "lua-ts"))]))
 
 ;;;###autoload
 (define-derived-mode lua-ts-mode prog-mode "Lua"
-  "Major mode for editing Lua files, powered by tree-sitter."
+  "Major mode for editing Lua files, powered by tree-sitter.
+
+\\{lua-ts-mode-map}"
   :syntax-table lua-ts--syntax-table
+  (use-local-map lua-ts-mode-map)
 
   (when (treesit-ready-p 'lua)
     (treesit-parser-create 'lua)
@@ -404,20 +642,39 @@ lua-ts-mode
                 (rx (or "function_declaration" "function_definition")))
     (setq-local treesit-thing-settings
                 `((lua
-                   (sentence ,(rx (or "do_statement"
-                                      "field"
-                                      "for_statement"
-                                      "function_call"
-                                      "if_statement"
-                                      "repeat_statement"
-                                      "return_statement"
-                                      "variable_declaration"
-                                      "while_statement")))
-                   (sexp ,(rx (or "arguments"
-                                  "block"
-                                  "parameters"
-                                  "string"
-                                  "table_constructor")))
+                   (function ,(rx (or "function_declaration"
+                                      "function_definition")))
+                   (keyword ,(regexp-opt lua-ts--keywords
+                                         'symbols))
+                   (loop-statement ,(rx (or "do_statement"
+                                            "for_statement"
+                                            "repeat_statement"
+                                            "while_statement")))
+                   (sentence (or function
+                                 loop-statement
+                                 ,(rx (or "assignment_statement"
+                                          "comment"
+                                          "field"
+                                          "function_call"
+                                          "if_statement"
+                                          "return_statement"
+                                          "variable_declaration"))))
+                   (sexp (or function
+                             keyword
+                             loop-statement
+                             ,(rx (or "arguments"
+                                      "break_statement"
+                                      "expression_list"
+                                      "false"
+                                      "identifier"
+                                      "nil"
+                                      "number"
+                                      "parameters"
+                                      "parenthesized_expression"
+                                      "string"
+                                      "table_constructor"
+                                      "true"
+                                      "vararg_expression"))))
                    (text "comment"))))
 
     ;; Imenu.
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..9797467bbe5 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -5,148 +5,675 @@ Code:
     (lua-ts-mode)
     (indent-region (point-min) (point-max)))
 
-Name: Basic Indent
+Name: Chunk Indent
 
 =-=
-      print(
-0,
-      1
-)
+ print(1)
+	print(2)
+=-=
+print(1)
+print(2)
+=-=-=
 
-local function f(o)
-   if o.x > o.y then
- return o.x
-elseif o.y > o.z then
-           return o.y
-      else
-return o.z
-            end
+Name: Function Indent
+
+=-=
+function f1(n)
+print(n)
+return n + 1
 end
 
-f({
- x = 1,
-  y = 2,
-   z = 3,
-})
+local function f2(n)
+print(n)
+return n * 2
+end
 
-;(function()
-return false
-)()
+local f3 = function(n)
+print(n)
+return n / 3
+end
+
+function f4(...)
+local f = function (...)
+if ok
+then print(1)
+else print(0)
+end
+end
+return f
+end
+
+function f5(...)
+local f = function (...)
+if ok
+then
+print(1)
+else
+print(0)
+end
+end
+return f
+end
+
+function f6(...)
+local f = function (...)
+if ok then
+print(1)
+else
+print(0)
+end
+end
+return f
+end
+
+;(function ()
+ return true
+ end)()
 =-=
-print(
-  0,
-  1
-)
+function f1(n)
+  print(n)
+  return n + 1
+end
 
-local function f(o)
-  if o.x > o.y then
-    return o.x
-  elseif o.y > o.z then
-    return o.y
-  else
-    return o.z
+local function f2(n)
+  print(n)
+  return n * 2
+end
+
+local f3 = function(n)
+  print(n)
+  return n / 3
+end
+
+function f4(...)
+  local f = function (...)
+    if ok
+    then print(1)
+    else print(0)
+    end
   end
+  return f
 end
 
-f({
-  x = 1,
-  y = 2,
-  z = 3,
-})
+function f5(...)
+  local f = function (...)
+    if ok
+    then
+      print(1)
+    else
+      print(0)
+    end
+  end
+  return f
+end
+
+function f6(...)
+  local f = function (...)
+    if ok then
+      print(1)
+    else
+      print(0)
+    end
+  end
+  return f
+end
 
-;(function()
-  return false
-)()
+;(function ()
+  return true
+end)()
 =-=-=
 
-Name: Argument Indent
+Name: Conditional Indent
 
 =-=
-function h(
-string,
-number,
-options)
-print(string, number, options)
+if true then
+print(true)
+return 1
+elseif false then
+print(false)
+return -1
+else
+print(nil)
+return 0
 end
 
-local p = h(
-"sring",
-	 1000,
-      {
-cost = 2,
-length = 8,
-	parallelism = 4,
-})
+if true
+ then
+ print(true)
+ return 1
+ elseif false
+ then
+ print(false)
+ return -1
+ else
+ print(nil)
+ return 0
+end
+
+if true
+ then return 1
+ elseif false
+ then return -1
+ else return 0
+end
 =-=
-function h(
-  string,
-  number,
-  options)
-  print(string, number, options)
+if true then
+  print(true)
+  return 1
+elseif false then
+  print(false)
+  return -1
+else
+  print(nil)
+  return 0
 end
 
-local p = h(
-  "sring",
-  1000,
-  {
-    cost = 2,
-    length = 8,
-    parallelism = 4,
-  })
+if true
+then
+  print(true)
+  return 1
+elseif false
+then
+  print(false)
+  return -1
+else
+  print(nil)
+  return 0
+end
+
+if true
+then return 1
+elseif false
+then return -1
+else return 0
+end
 =-=-=
 
-Name: Continuation Indent
+Name: Loop Indent
 
 =-=
+for k,v in pairs({}) do
+ print(k)
+ print(v)
+end
+
+for i=1,10
+ do print(i)
+end
+
+while n < 10 do
+ n = n + 1
+ print(n)
+end
+
+while n < 10
+ do
+ n = n + 1
+ print(n)
+end
+
+for i=0,9 do
+repeat n = n+1
+ until n > 99
+end
+
+repeat
+z = z * 2
+print(z)
+until z > 12
+
+ for i,x in ipairs(t) do
+ while i < 9
+ do
+ local n = t[x]
+ repeat n = n + 1
+ until n > #t
+ while n < 99
+ do
+ print(n)
+ end
+ end
+ print(t[i])
+ end
+
+do
+local a = b
+print(a + 1)
+end
+=-=
+for k,v in pairs({}) do
+  print(k)
+  print(v)
+end
+
+for i=1,10
+do print(i)
+end
+
+while n < 10 do
+  n = n + 1
+  print(n)
+end
+
+while n < 10
+do
+  n = n + 1
+  print(n)
+end
+
+for i=0,9 do
+  repeat n = n+1
+  until n > 99
+end
+
+repeat
+  z = z * 2
+  print(z)
+until z > 12
+
+for i,x in ipairs(t) do
+  while i < 9
+  do
+    local n = t[x]
+    repeat n = n + 1
+    until n > #t
+    while n < 99
+    do
+      print(n)
+    end
+  end
+  print(t[i])
+end
+
+do
+  local a = b
+  print(a + 1)
+end
+=-=-=
+
+Name: Bracket Indent
+
+=-=
+fn(
+  )
+
+tb={
+   }
+=-=
+fn(
+)
+
+tb={
+}
+=-=-=
+
+Name: Multi-line String Indent
+
+=-=
+local s = [[
+      Multi-line
+    string content
+    ]]
+
 function f()
   local str = [[
   multi-line
 	string
     ]]
---[[
-multi-line
-comment
-    ]]
 return true
 end
 =-=
+local s = [[
+      Multi-line
+    string content
+    ]]
+
 function f()
   local str = [[
   multi-line
 	string
     ]]
-  --[[
+  return true
+end
+=-=-=
+
+Name: Multi-line Comment Indent
+
+=-=
+--[[
+      Multi-line
+    comment content
+ ]]
+
+function f()
+--[[
+multi-line
+   comment
+    ]]
+  return true
+end
+=-=
+--[[
+      Multi-line
+    comment content
+ ]]
+
+function f()
+--[[
 multi-line
-comment
+   comment
     ]]
   return true
 end
 =-=-=
 
-Name: Loop Indent
+Name: Argument Indent
+
+=-=
+ h(
+ "string",
+ 1000
+ )
+
+local p = h(
+"string",
+	 1000
+)
+
+fn(1,
+2,
+     3)
+
+fn( 1, 2,
+3, 4 )
+
+f({
+x = 1,
+y = 2,
+z = 3,
+})
+
+f({ x = 1,
+y = 2,
+z = 3, })
+
+Test({
+a=1
+})
+
+Test({
+a = 1,
+b = 2,
+},
+nil)
+=-=
+h(
+  "string",
+  1000
+)
+
+local p = h(
+  "string",
+  1000
+)
+
+fn(1,
+   2,
+   3)
+
+fn( 1, 2,
+    3, 4 )
+
+f({
+  x = 1,
+  y = 2,
+  z = 3,
+})
+
+f({ x = 1,
+    y = 2,
+    z = 3, })
+
+Test({
+  a=1
+})
+
+Test({
+       a = 1,
+       b = 2,
+     },
+     nil)
+=-=-=
+
+Name: Parameter Indent
 
 =-=
-for k, v in pairs({}) do
-	 print(k, v)
+function f1(
+a,
+b
+)
+print(a,b)
 end
 
-while n < 10 do
-n = n + 1
+local function f2(a,
+                b)
+print(a,b)
 end
 
-repeat
-z = z * 2
- until z > 12
+local f3 = function( a, b,
+                c, d )
+print(a,b,c,d)
+end
 =-=
-for k, v in pairs({}) do
-  print(k, v)
+function f1(
+  a,
+  b
+)
+  print(a,b)
 end
 
-while n < 10 do
-  n = n + 1
+local function f2(a,
+                  b)
+  print(a,b)
 end
 
-repeat
-  z = z * 2
-until z > 12
+local f3 = function( a, b,
+                     c, d )
+  print(a,b,c,d)
+end
+=-=-=
+
+Name: Table Indent
+
+=-=
+local Other = {
+    First={up={Step=true,Jump=true},
+        down={Step=true,Jump=true},
+        left={Step=true,Jump=true},
+        right={Step=true,Jump=true}},
+    Second={up={Step=true,Jump=true},
+        down={Step=true,Jump=true},
+        left={Step=true,Jump=true},
+        right={Step=true,Jump=true}},
+    Third={up={Goto=true},
+        down={Goto=true},
+        left={Goto=true},
+        right={Goto=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Other = {
+  First={up={Step=true,Jump=true},
+         down={Step=true,Jump=true},
+         left={Step=true,Jump=true},
+         right={Step=true,Jump=true}},
+  Second={up={Step=true,Jump=true},
+          down={Step=true,Jump=true},
+          left={Step=true,Jump=true},
+          right={Step=true,Jump=true}},
+  Third={up={Goto=true},
+         down={Goto=true},
+         left={Goto=true},
+         right={Goto=true}}
+}
+
+local Other = {
+  a = 1,
+  b = 2,
+  c = 3,
+}
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: End Indent
+
+=-=
+function f(x)
+    for y=1,x.y do
+        for x=1,x.z do
+            if x.y and x.z then
+                if y <= x then
+                    y = y + 1
+   end end end end
+    return {x,y} or {math.random(),math.random()}
+ end
+
+for y=1,x.y do
+    for x=1,x.z do
+        if x.y and x.z then
+            if y <= x then
+                y = y + 1
+           end
+ end end end
+=-=
+function f(x)
+    for y=1,x.y do
+        for x=1,x.z do
+            if x.y and x.z then
+                if y <= x then
+                    y = y + 1
+    end end end end
+    return {x,y} or {math.random(),math.random()}
+end
+
+for y=1,x.y do
+    for x=1,x.z do
+        if x.y and x.z then
+            if y <= x then
+                y = y + 1
+            end
+end end end
+=-=-=
+
+Name: Nested Function Indent
+
+=-=
+function a(...)
+    return (function (x)
+                return x
+            end)(foo(...))
+end
+
+function b(n)
+    local x = 1
+    return function (i)
+        return function (...)
+            return (function (n, ...)
+                return function (f, ...)
+                    return (function (...)
+                        if ... and x < 9 then
+                            x = x + 1
+                            return ...
+                    end end)(n(f, ...))
+                end, ...
+            end)(i(...))
+end end end
+
+function c(f)
+    local f1 = function (...)
+        if nil ~= ... then
+            return f(...)
+        end
+    end
+    return function (i)
+        return function (...)
+            local fn = function (n, ...)
+                local x = function (f, ...)
+                    return f1(n(f, ...))
+                end
+                return x
+            end
+            return fn(i(...))
+        end
+    end
+end
+
+function d(f)
+    local f1 = function (c, f, ...)
+        if ... then
+            if f(...) then
+                return ...
+            else
+                return c(f, ...)
+    end end end
+    return function (i)
+        return function (...)
+            return (function (n, ...)
+                local function j (f, ...)
+                    return f1(j, f, n(f, ...))
+                end
+                return j, ...
+            end)(i(...))
+end end end
+
+function e (n, t)
+    return function (i)
+        return function (...)
+            return (
+                function (n, ...)
+                    local x, y, z = 0, {}
+                    return (function (f, ...)
+                        return (function (i, ...) return i(i, ...) end)(
+                            function (i, ...)
+                                return f(function (x, ...)
+                                             return i(i, ...)(x, ...)
+                                         end, ...)
+                            end)
+                    end)(function (j)
+                        return function(f, ...)
+                            return (function (c, f, ...)
+                                if ... then
+                                    if n+1 == x then
+                                        local y1, x1 = y, x
+                                        y, x = {}, 0
+                                        return (function (...)
+                                                    z = ...
+                                                    return ...
+                                                end)(t(y1-1, x1-1, ...))
+                                    else
+                                        x = x - 1
+                                        return c(f,
+                                                 (function (...)
+                                                      z = ...
+                                                      return ...
+                                                  end)(t(y, x, ...)))
+                                    end
+                                elseif x ~= 0 then
+                                    x = 0
+                                    return z, y
+                            end end)(j, f, n(f, ...))
+                    end end), ...
+                end)(i(...))
+end end end
 =-=-=
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
index 770aa23b18d..11e86f12926 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
@@ -147,7 +147,7 @@ end|
 print(1)
 =-=-=
 
-Name: forward-sentence moves over for statements
+Name: forward-sentence moves over do statements
 
 =-=
 |do
@@ -417,34 +417,6 @@ Code:
 
 Point-Char: |
 
-Name: forward-sexp moves over blocks
-
-=-=
-local function Test()
-  |local t = {
-    a = 1,
-  }
-
-  if true then
-    print(1)
-  else
-    print(0)
-  end
-end
-=-=
-local function Test()
-  local t = {
-    a = 1,
-  }
-
-  if true then
-    print(1)
-  else
-    print(0)
-  end|
-end
-=-=-=
-
 Name: forward-sexp moves over arguments
 
 =-=
@@ -481,41 +453,91 @@ local t = { 1,
   3 }|
 =-=-=
 
-Code:
-  (lambda ()
-    (lua-ts-mode)
-    (backward-sexp 1))
+Name: forward-sexp moves over parenthesized expressions
 
-Point-Char: |
+=-=
+|(function (x) return x + 1 end)(41)
+=-=
+(function (x) return x + 1 end)|(41)
+=-=-=
 
-Name: backward-sexp moves over blocks
+Name: forward-sexp moves over function declarations
 
 =-=
-local function Test()
-  local t = {
-    a = 1,
-  }
+|function foo (x)
+    if false then
+        print "foo"
+    elseif true then
+        print "bar"
+    end
+end
+=-=
+function foo (x)
+    if false then
+        print "foo"
+    elseif true then
+        print "bar"
+    end
+end|
+=-=-=
 
-  if true then
-    print(1)
-  else
-    print(0)
-  end|
+Name: forward-sexp moves over do statements
+
+=-=
+|do
+  print(a + 1)
 end
 =-=
-local function Test()
-  |local t = {
-    a = 1,
-  }
+do
+  print(a + 1)
+end|
+=-=-=
 
-  if true then
-    print(1)
-  else
-    print(0)
-  end
+Name: forward-sexp moves over for statements
+
+=-=
+|for k,v in pairs({}) do
+  print(k, v)
+end
+=-=
+for k,v in pairs({}) do
+  print(k, v)
+end|
+=-=-=
+
+Name: forward-sexp moves over repeat statements
+
+=-=
+|repeat
+  n = n + 1
+until n > 10
+=-=
+repeat
+  n = n + 1
+until n > 10|
+=-=-=
+
+Name: forward-sexp moves over while statements
+
+=-=
+|while n < 99
+do
+  n = n+1
 end
+=-=
+while n < 99
+do
+  n = n+1
+end|
 =-=-=
 
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (backward-sexp 1))
+
+Point-Char: |
+
 Name: backward-sexp moves over arguments
 
 =-=
@@ -551,3 +573,31 @@ local t = |{ 1,
   2,
   3 }
 =-=-=
+
+Name: backward-sexp moves over parenthesized expressions
+
+=-=
+(function (x) return x + 1 end)|(41)
+=-=
+|(function (x) return x + 1 end)(41)
+=-=-=
+
+Name: backward-sexp moves over function declarations
+
+=-=
+function foo (x)
+    if false then
+        print "foo"
+    elseif true then
+        print "bar"
+    end
+end|
+=-=
+|function foo (x)
+    if false then
+        print "foo"
+    elseif true then
+        print "bar"
+    end
+end
+=-=-=
-- 
2.41.0