unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
@ 2023-09-22 19:17 Andrey Listopadov
  2023-09-22 19:49 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: Andrey Listopadov @ 2023-09-22 19:17 UTC (permalink / raw)
  To: 66159


I've been editing Lua using the long-existing lua-mode[1] and it works
well enough for me, but it's indentation implementation is very slow.
Indenting a file with just 600 lines takes several seconds on my
machine.  So when I saw that Emacs now features a lua-ts-mode package in
its core, I tried to move to it, in hopes that it would be faster.

However, a lot of code is now indented differently, not how I used to,
and this creates a lot of noise in commits, as I often re-indent the
whole buffer before saving the file. Here are some examples.

lua-mode:

function foo (e)
    if e == nil
    then return 1000
    else return e
end end

lua-ts-mode:

function foo (e)
    if e == nil
        then return 1000
    else return e
    end end

My thoughts:

`then' is indented as the part of the block of code that is part of the
`true' branch itself. In lua-mode, it is indented the same as `if' itself,
and everything after `then' is indented if moved to a new line:

function foo (e)
    if e == nil
    then
        return 1000
    else
        return e
end end

In lua-ts-mode it is indented together with the body which isn't
idiomatic:

function foo (e)
    if e == nil
        then
        return 1000
    else
        return e
    end end

Another thing that bothers me is that I prefer Gassanenko-style packing
of `end' keywords so that they vertically align with the scope of the
opened block, as it saves so much vertical space and is easier for me to
read, but lua-ts-mode moves it to the latest innermost indentation
level, as opposed to the outermost depending on the count of ends in the
line itself:

function lowest_entropy_cell(world)
|   local lowest,res=math.huge,nil
|   for y=1,world.height do
|   |   for x=1,world.width do
|   |   |   local cell=world:get(x,y)
|   |   |   if cell.is_set then
|   |   |   |   local e=cell_enthropy(cell)
|   |   |   |   trace(e)
|   |   |   |   if e <= lowest then
|   |   |   |   |   lowest,res=e,{x,y}
|   end end end end
|   return res or {math.random(world.width),math.random(world.height)}
end

However, lua-ts-mode indents it like so:

function lowest_entropy_cell(world)
    local lowest,res=math.huge,nil
    for y=1,world.height do
        for x=1,world.width do
            local cell=world:get(x,y)
            if cell.is_set then
                local e=cell_enthropy(cell)
                trace(e)
                if e <= lowest then
                    lowest,res=e,{x,y}
                end end end end
    return res or {math.random(world.width),math.random(world.height)}
end

I noticed the difference in table indentations as well.

lua-mode:

local Recipe = {
    Floor={up={Floor=true,Wall=true},
           down={Floor=true,Wall=true},
           left={Floor=true,Wall=true},
           right={Floor=true,Wall=true}},
    Wall={up={Floor=true,Wall=true},
          down={Floor=true,Wall=true},
          left={Floor=true,Wall=true},
          right={Floor=true,Wall=true}},
    Corridor={up={Corridoor=true},
              down={Corridoor=true},
              left={Corridoor=true},
              right={Corridoor=true}}
}

lua-ts-mode:

local Recipe = {
    Floor={up={Floor=true,Wall=true},
        down={Floor=true,Wall=true},
        left={Floor=true,Wall=true},
        right={Floor=true,Wall=true}},
    Wall={up={Floor=true,Wall=true},
        down={Floor=true,Wall=true},
        left={Floor=true,Wall=true},
        right={Floor=true,Wall=true}},
    Corridor={up={Corridoor=true},
        down={Corridoor=true},
        left={Corridoor=true},
        right={Corridoor=true}}
}

The first one is more logical and obeys scope levels.

Should I expect this to be the default new way of indenting or the
indentation can be made to match the lua-mode where it is sensible?

Thanks!

P.S. I know that stacking `end' isn't really idiomatic Lua style, but it
works in old-lua mode sensible enough that it is actually surprising,
and transforms Lua into code that is easy to parse just by looking on
the indentation level, much like Lisps.

[1] https://github.com/immerrr/lua-mode


In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.38, cairo version 1.17.8) of 2023-09-18 built on toolbox
Repository revision: 861f9cb78370e2b78f852e5ccde9b63c94486ca8
Repository branch: master
System Description: Fedora Linux 38 (Container Image)

Configured using:
 'configure --without-compress-install --with-native-compilation=aot
 --with-pgtk --with-mailutils --with-xwidgets
 --prefix=/var/home/alist/.local'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSELINUX LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY
PDUMPER PGTK PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER XIM XWIDGETS GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Lua

Minor modes in effect:
  repeat-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  electric-pair-mode: t
  hl-todo-mode: t
  savehist-mode: t
  delete-selection-mode: t
  pixel-scroll-precision-mode: t
  global-auto-revert-mode: t
  corfu-popupinfo-mode: t
  global-corfu-mode: t
  corfu-mode: t
  global-region-bindings-mode: t
  recentf-mode: t
  gcmh-mode: t
  server-mode: t
  marginalia-mode: t
  vertico-mode: t
  override-global-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  context-menu-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  abbrev-mode: t
  hs-minor-mode: t

Load-path shadows:
/var/home/alist/.config/emacs/elpa/transient-20230915.1911/transient hides /var/home/alist/.local/share/emacs/30.0.50/lisp/transient
/var/home/alist/.config/emacs/elpa/modus-themes-20230916.905/theme-loaddefs hides /var/home/alist/.local/share/emacs/30.0.50/lisp/theme-loaddefs

Features:
(shadow emacsbug dabbrev lua-ts-mode rect lua-mode modus-operandi-theme
python geiser-mode geiser-xref geiser-compile geiser-guile tramp
trampver tramp-integration tramp-message tramp-compat tramp-loaddefs
info-look geiser geiser-debug geiser-repl geiser-image geiser-capf
geiser-doc geiser-menu geiser-autodoc geiser-edit etags fileloop
geiser-completion geiser-eval geiser-connection tq geiser-syntax
geiser-log geiser-popup geiser-impl help-fns radix-tree geiser-custom
geiser-base scheme find-dired grep display-line-numbers css-mode treesit
smie mu4e-alert advice time ht alert log4e notifications gntp mu4e
mu4e-org mu4e-main mu4e-view mu4e-headers mu4e-compose mu4e-draft
mu4e-actions smtpmail mu4e-search mu4e-lists mu4e-bookmarks mu4e-mark
mu4e-message flow-fill hl-line mu4e-contacts mu4e-update mu4e-folders
mu4e-server mu4e-context mu4e-vars mu4e-helpers mu4e-config ido tabify
cus-start repeat files-x org-archive puni pulse color consult ox-odt
rng-loc rng-uri rng-parse rng-match rng-dt rng-util rng-pttrn nxml-parse
nxml-ns nxml-enc xmltok nxml-util ox-icalendar org-agenda ox-ascii
ox-latex ox-hugo ox-hugo-deprecated ffap ox-blackfriday ox-md ox-html
table ox-publish tomelr ox sgml-mode facemenu xwidget magit-bookmark
bookmark goto-addr vc-hg vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs
log-view vc bug-reference vc-git vc-dispatcher misearch multi-isearch
dired-aux face-remap magit-extras orderless flyspell ispell org-element
org-persist org-id org-refile avl-tree generator oc-basic ol-eww eww xdg
url-queue mm-url ol-rmail ol-mhe ol-irc ol-info ol-gnus nnselect
gnus-art mm-uu mml2015 mm-view mml-smime smime gnutls dig gnus-sum shr
pixel-fill kinsoku url-file svg dom gnus-group gnus-undo gnus-start
gnus-dbus gnus-cloud nnimap nnmail mail-source utf7 nnoo parse-time
gnus-spec gnus-int gnus-range gnus-win gnus nnheader range ol-docview
doc-view jka-compr image-mode exif ol-bibtex bibtex iso8601 ol-bbdb
ol-w3m ol-doi org-link-doi org-tempo tempo ob-fennel fennel-proto-repl
fennel-mode inf-lisp xref magit-submodule magit-blame magit-stash
magit-reflog magit-bisect magit-push magit-pull magit-fetch magit-clone
magit-remote magit-commit magit-sequence magit-notes magit-worktree
magit-tag magit-merge magit-branch magit-reset magit-files magit-refs
magit-status magit magit-repos magit-apply magit-wip magit-log
which-func imenu magit-diff smerge-mode diff diff-mode git-commit
log-edit message sendmail yank-media puny dired dired-loaddefs rfc822
mml mml-sec epa epg rfc6068 epg-config gnus-util mm-decode mm-bodies
mm-encode mail-parse rfc2231 rfc2047 rfc2045 mm-util ietf-drums
mail-prsvr mailabbrev mail-utils gmm-utils mailheader pcvs-util add-log
magit-core magit-autorevert magit-margin magit-transient magit-process
with-editor magit-mode transient magit-git magit-base magit-section
cursor-sensor crm project ob-lua ob-shell shell org ob ob-tangle ob-ref
ob-lob ob-table ob-exp org-macro org-src ob-comint org-pcomplete
pcomplete org-list org-footnote org-faces org-entities ob-emacs-lisp
ob-core ob-eval org-cycle org-table org-keys oc org-loaddefs find-func
cal-menu calendar cal-loaddefs vertico-directory mule-util time-date
noutline outline elec-pair isayt disp-table hideshow hl-todo savehist
delsel pixel-scroll cua-base autorevert filenotify corfu-popupinfo cape
corfu region-bindings recentf tree-widget gcmh init proxy gsettings s
gvariant parsec dash zig-compilation-mode fennel-compilation-mode
clojure-compilation-mode derived compile text-property-search server
ediff ediff-merg ediff-mult ediff-wind ediff-diff ediff-help ediff-init
ediff-util sql-indent sql view thingatpt comint ansi-osc ring zig-mode
reformatter ansi-color ol org-fold org-fold-core org-compat org-version
org-macs format-spec blog marginalia vertico compat use-package-delight
formfeed modus-vivendi-theme modus-themes dbus xml common-lisp-modes
novice cus-edit pp cus-load wid-edit font mode-line edmacro kmacro
messages use-package-bind-key bind-key defaults functions local-config
delight comp comp-cstr warnings icons rx use-package-ensure cl-extra
help-mode use-package-core early-init cape-autoloads
clj-decompiler-autoloads clj-refactor-autoloads cider-autoloads
clojure-mode-autoloads common-lisp-modes-autoloads consult-autoloads
corfu-terminal-autoloads corfu-autoloads csv-mode-autoloads
delight-autoloads dumb-jump-autoloads eat-autoloads elfeed-autoloads
expand-region-autoloads fennel-mode-autoloads gcmh-autoloads
geiser-guile-autoloads geiser-autoloads gsettings-autoloads
gvariant-autoloads hl-todo-autoloads inflections-autoloads
isayt-autoloads jdecomp-autoloads lisp-extra-font-lock-autoloads
lsp-java-autoloads lsp-metals-autoloads dap-mode-autoloads
lsp-docker-autoloads bui-autoloads lsp-treemacs-autoloads
lsp-mode-autoloads f-autoloads lua-mode-autoloads marginalia-autoloads
markdown-mode-autoloads message-view-patch-autoloads magit-autoloads
pcase magit-section-autoloads git-commit-autoloads
modus-themes-autoloads mu4e-alert-autoloads alert-autoloads
log4e-autoloads gntp-autoloads multiple-cursors-autoloads
orderless-autoloads org-modern-autoloads org-tree-slide-autoloads
ox-hugo-autoloads package-lint-flymake-autoloads package-lint-autoloads
paredit-autoloads parsec-autoloads parseedn-autoloads parseclj-autoloads
phi-search-autoloads poly-org-autoloads polymode-autoloads
popon-autoloads popup-autoloads puni-autoloads easy-mmode finder-inf
queue-autoloads racket-mode-autoloads region-bindings-autoloads
request-autoloads scala-mode-autoloads separedit-autoloads
edit-indirect-autoloads sesman-autoloads sly-autoloads spinner-autoloads
sql-indent-autoloads tomelr-autoloads transient-autoloads
treemacs-autoloads cfrs-autoloads posframe-autoloads ht-autoloads
hydra-autoloads lv-autoloads pfuture-autoloads ace-window-autoloads
avy-autoloads s-autoloads dash-autoloads vertico-autoloads
vundo-autoloads with-editor-autoloads info compat-autoloads
yaml-autoloads yaml-mode-autoloads yasnippet-autoloads
zig-mode-autoloads reformatter-autoloads package browse-url url
url-proxy url-privacy url-expand url-methods url-history url-cookie
generate-lisp-file url-domsuf url-util mailcap url-handlers url-parse
auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x
map byte-opt gv bytecomp byte-compile url-vars cl-loaddefs cl-lib rmc
iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode mwheel term/pgtk-win pgtk-win
term/common-win pgtk-dnd touch-screen tool-bar dnd fontset image
regexp-opt fringe tabulated-list replace newcomment text-mode lisp-mode
prog-mode register page tab-bar menu-bar rfn-eshadow isearch easymenu
timer select scroll-bar mouse jit-lock font-lock syntax font-core
term/tty-colors frame minibuffer nadvice seq simple cl-generic
indonesian philippine cham georgian utf-8-lang misc-lang vietnamese
tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
composite emoji-zwj charscript charprop case-table epa-hook
jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button loaddefs
theme-loaddefs faces cus-face macroexp files window text-properties
overlay sha1 md5 base64 format env code-pages mule custom widget keymap
hashtable-print-readable backquote threads xwidget-internal dbusbind
inotify dynamic-setting system-font-setting font-render-setting cairo
gtk pgtk lcms2 multi-tty move-toolbar make-network-process
native-compile emacs)

Memory information:
((conses 16 1147781 1425356) (symbols 48 52742 50)
 (strings 32 272573 78657) (string-bytes 1 9908365)
 (vectors 16 119200) (vector-slots 8 2827929 951125)
 (floats 8 888 21739) (intervals 56 40788 17575) (buffers 992 46))

-- 
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-22 19:17 bug#66159: 30.0.50; lua-ts-mode semantic indentation problems Andrey Listopadov
@ 2023-09-22 19:49 ` Eli Zaretskii
  2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-06 19:44 ` bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159) john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2023-09-22 19:49 UTC (permalink / raw)
  To: Andrey Listopadov, john muhl; +Cc: 66159

> From: Andrey Listopadov <andreyorst@gmail.com>
> Date: Fri, 22 Sep 2023 22:17:55 +0300
> 
> 
> I've been editing Lua using the long-existing lua-mode[1] and it works
> well enough for me, but it's indentation implementation is very slow.
> Indenting a file with just 600 lines takes several seconds on my
> machine.  So when I saw that Emacs now features a lua-ts-mode package in
> its core, I tried to move to it, in hopes that it would be faster.
> 
> However, a lot of code is now indented differently, not how I used to,
> and this creates a lot of noise in commits, as I often re-indent the
> whole buffer before saving the file. Here are some examples.

John, could you please look into this?





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-22 19:17 bug#66159: 30.0.50; lua-ts-mode semantic indentation problems Andrey Listopadov
  2023-09-22 19:49 ` Eli Zaretskii
@ 2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-24 15:44   ` Eli Zaretskii
  2023-09-24 16:38   ` Andrey Listopadov
  2023-10-06 19:44 ` bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159) john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2 siblings, 2 replies; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-24 15:06 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159

[-- Attachment #1: Type: text/plain, Size: 2157 bytes --]

Andrey Listopadov <andreyorst@gmail.com> writes:

> I've been editing Lua using the long-existing lua-mode[1] and it works
> well enough for me, but it's indentation implementation is very slow.
> Indenting a file with just 600 lines takes several seconds on my
> machine.  So when I saw that Emacs now features a lua-ts-mode package
> in its core, I tried to move to it, in hopes that it would be faster.

Thanks for trying.

> However, a lot of code is now indented differently, not how I used to,
> and this creates a lot of noise in commits, as I often re-indent the
> whole buffer before saving the file. Here are some examples.

The "then" and table examples should indent how you like with the
attached patch. Arguments and parameters now also work like your table
example so you can have:

  local function fn(a, b, c,
                    another) end

  fn(1,
     2,
     3,
     {a="first",
      b="second"})

> Another thing that bothers me is that I prefer Gassanenko-style packing
> of `end' keywords so that they vertically align with the scope of the
> opened block, as it saves so much vertical space and is easier for me to
> read, but lua-ts-mode moves it to the latest innermost indentation
> level, as opposed to the outermost depending on the count of ends in the
> line itself:
>
> function lowest_entropy_cell(world)
> |   local lowest,res=math.huge,nil
> |   for y=1,world.height do
> |   |   for x=1,world.width do
> |   |   |   local cell=world:get(x,y)
> |   |   |   if cell.is_set then
> |   |   |   |   local e=cell_enthropy(cell)
> |   |   |   |   trace(e)
> |   |   |   |   if e <= lowest then
> |   |   |   |   |   lowest,res=e,{x,y}
> |   end end end end
> |   return res or {math.random(world.width),math.random(world.height)}
> end

I don't see any reason not to support that style but I'm not sure how to
do it. A patch would be welcome but I'll try to figure it out sometime.

> Should I expect this to be the default new way of indenting or the
> indentation can be made to match the lua-mode where it is sensible?

Sure. It's a new mode so nothing is really set in stone. Let me know if
you have other suggestions.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-indentation-in-lus-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 5287 bytes --]

From d24d1d128fd29ead355334612ccca3f5e2e1fbd2 Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
Date: Sat, 23 Sep 2023 10:49:11 -0500
Subject: [PATCH] Improve indentation in lus-ts-mode (Bug#66159)

- Align "then" with if/elseif/else/end when it appears at the
  beginning of a line.

- Anchor indent of the first child in arguments, parameters and
  tables to the parent and the other children to the first.

* lisp/progmodes/lua-ts-mode.el (lua-ts--first-child)
(lua-ts--simple-indent-rules): Improve indentation.
* test/lisp/progmodes/lua-ts-mode-resources/indent.erts: Add
tests.
---
 lisp/progmodes/lua-ts-mode.el                 |  17 ++-
 .../lua-ts-mode-resources/indent.erts         | 125 ++++++++++++++++++
 2 files changed, 139 insertions(+), 3 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..3d946dd1de2 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -233,6 +233,10 @@ lua-ts--font-lock-settings
    '((ERROR) @font-lock-warning-face))
   "Tree-sitter font-lock settings for `lua-ts-mode'.")
 
+(defun lua-ts--first-child (node _p _b &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
 (defvar lua-ts--simple-indent-rules
   `((lua
      ((parent-is "chunk") column-0 0)
@@ -240,6 +244,7 @@ lua-ts--simple-indent-rules
      ((parent-is "block") parent-bol 0)
      ((node-is "}") parent-bol 0)
      ((node-is ")") parent-bol 0)
+     ((node-is "then") standalone-parent 0)
      ((node-is "else_statement") parent-bol 0)
      ((node-is "elseif_statement") parent-bol 0)
      ((node-is "end") parent-bol 0)
@@ -251,9 +256,15 @@ lua-ts--simple-indent-rules
      ((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)
+     ((and (parent-is "table_constructor") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") (nth-sibling 1) 0)
+     ((and (parent-is "arguments") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") (nth-sibling 1) 0)
+     ((and (parent-is "parameters") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") (nth-sibling 1) 0)
      ((parent-is "ERROR") no-indent 0))))
 
 (defvar lua-ts--syntax-table
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..999e01e3e37 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -32,6 +32,22 @@ f({
 ;(function()
 return false
 )()
+
+function foo (e)
+    if e == nil
+      then return 1000
+    else return e
+    end
+end
+
+function foo (e)
+    if e == nil
+    then
+        return 1000
+    else
+        return e
+    end
+end
 =-=
 print(
   0,
@@ -57,6 +73,22 @@ f({
 ;(function()
   return false
 )()
+
+function foo (e)
+  if e == nil
+  then return 1000
+  else return e
+  end
+end
+
+function foo (e)
+  if e == nil
+  then
+    return 1000
+  else
+    return e
+  end
+end
 =-=-=
 
 Name: Argument Indent
@@ -77,6 +109,13 @@ cost = 2,
 length = 8,
 	parallelism = 4,
 })
+
+fn(1,
+2,
+     3)
+
+fn(1, 2,
+3)
 =-=
 function h(
   string,
@@ -93,6 +132,13 @@ local p = h(
     length = 8,
     parallelism = 4,
   })
+
+fn(1,
+   2,
+   3)
+
+fn(1, 2,
+   3)
 =-=-=
 
 Name: Continuation Indent
@@ -150,3 +196,82 @@ repeat
   z = z * 2
 until z > 12
 =-=-=
+
+Name: Parameter Indent
+
+=-=
+fn(a,
+b) end
+
+fn(a, b,
+c) end
+
+fn(
+a,
+b
+) end
+=-=
+fn(a,
+   b) end
+
+fn(a, b,
+   c) end
+
+fn(
+  a,
+  b
+) end
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Table Indent
+
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+        down={Corridoor=true},
+        left={Corridoor=true},
+        right={Corridoor=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+           down={Floor=true,Wall=true},
+           left={Floor=true,Wall=true},
+           right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+          down={Floor=true,Wall=true},
+          left={Floor=true,Wall=true},
+          right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+              down={Corridoor=true},
+              left={Corridoor=true},
+              right={Corridoor=true}}
+}
+
+local Other = {
+    a = 1,
+    b = 2,
+    c = 3,
+}
+=-=-=
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-24 15:44   ` Eli Zaretskii
  2023-09-24 16:38   ` Andrey Listopadov
  1 sibling, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2023-09-24 15:44 UTC (permalink / raw)
  To: john muhl; +Cc: andreyorst, 66159

> Cc: 66159@debbugs.gnu.org
> Date: Sun, 24 Sep 2023 10:06:50 -0500
> From:  john muhl via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
> 
> > Another thing that bothers me is that I prefer Gassanenko-style packing
> > of `end' keywords so that they vertically align with the scope of the
> > opened block, as it saves so much vertical space and is easier for me to
> > read, but lua-ts-mode moves it to the latest innermost indentation
> > level, as opposed to the outermost depending on the count of ends in the
> > line itself:
> >
> > function lowest_entropy_cell(world)
> > |   local lowest,res=math.huge,nil
> > |   for y=1,world.height do
> > |   |   for x=1,world.width do
> > |   |   |   local cell=world:get(x,y)
> > |   |   |   if cell.is_set then
> > |   |   |   |   local e=cell_enthropy(cell)
> > |   |   |   |   trace(e)
> > |   |   |   |   if e <= lowest then
> > |   |   |   |   |   lowest,res=e,{x,y}
> > |   end end end end
> > |   return res or {math.random(world.width),math.random(world.height)}
> > end
> 
> I don't see any reason not to support that style but I'm not sure how to
> do it. A patch would be welcome but I'll try to figure it out sometime.

Maybe introduce indentation styles into lua-ts-mode, like CC Mode and
c-ts-mode have?





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-24 15:44   ` Eli Zaretskii
@ 2023-09-24 16:38   ` Andrey Listopadov
  2023-09-24 18:20     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 25+ messages in thread
From: Andrey Listopadov @ 2023-09-24 16:38 UTC (permalink / raw)
  To: john muhl; +Cc: 66159


Thanks! The patch seems to work as I would expect for most cases.

> I don't see any reason not to support that style but I'm not sure how to
> do it. A patch would be welcome but I'll try to figure it out sometime.

A far as I understand it, in the `lua-mode' the overall line indentation
is computed via subtracting indentation for every `end' in that line,
e.g. `end end end' subtracts `lua-indent-level three times from current
indent level.

> Sure. It's a new mode so nothing is really set in stone. Let me know if
> you have other suggestions.

I also noticed that `lua-ts-inferior-lua` for some reason starts itself
in a new frame instead of just a new window. I haven't seen this
behavior with other inferior-*-modes, and the `lua-start-process'
function from `lua-mode' doesn't spawn a new frame either.

Looking at the code, it seems that it was a deliberate choice, as I see
the use of `display-buffer-pop-up-frame' in the `display-buffer' ACTION
arg.

--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-24 16:38   ` Andrey Listopadov
@ 2023-09-24 18:20     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-26 19:21       ` Andrey Listopadov
  2023-09-30  7:52       ` Philip Kaludercic
  0 siblings, 2 replies; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-24 18:20 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159, Philip Kaludercic, Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 2481 bytes --]

Eli Zaretskii <eliz@gnu.org> writes:

>> > Another thing that bothers me is that I prefer Gassanenko-style packing
>> > of `end' keywords so that they vertically align with the scope of the
>> > opened block, as it saves so much vertical space and is easier for me to
>> > read, but lua-ts-mode moves it to the latest innermost indentation
>> > level, as opposed to the outermost depending on the count of ends in the
>> > line itself:
>> 
>> I don't see any reason not to support that style but I'm not sure how to
>> do it. A patch would be welcome but I'll try to figure it out sometime.
>
> Maybe introduce indentation styles into lua-ts-mode, like CC Mode and
> c-ts-mode have?

I’ll have a look at what the c-ts-mode styles do and see what might be
applicable. In this case the changes can be accommodated by default.

Andrey Listopadov <andreyorst@gmail.com> writes:

> Thanks! The patch seems to work as I would expect for most cases.
>
>> I don't see any reason not to support that style but I'm not sure how to
>> do it. A patch would be welcome but I'll try to figure it out sometime.
>
> A far as I understand it, in the `lua-mode' the overall line indentation
> is computed via subtracting indentation for every `end' in that line,
> e.g. `end end end' subtracts `lua-indent-level three times from current
> indent level.

Thanks for the explanation. The attached patch should make end packing
work now.

>> Sure. It's a new mode so nothing is really set in stone. Let me know if
>> you have other suggestions.
>
> I also noticed that `lua-ts-inferior-lua` for some reason starts itself
> in a new frame instead of just a new window. I haven't seen this
> behavior with other inferior-*-modes, and the `lua-start-process'
> function from `lua-mode' doesn't spawn a new frame either.
>
> Looking at the code, it seems that it was a deliberate choice, as I see
> the use of `display-buffer-pop-up-frame' in the `display-buffer' ACTION
> arg.

It did use a window originally but I was asked to change it:

>> +    (pop-to-buffer buffer)))
>
> I believe that `display-buffer-pop-up-frame' is the preferred alternative
> to `pop-to-buffer' these days.

Maybe I misunderstood and it was only about not using ‘pop-to-buffer’
and should have been ‘display-buffer-pop-up-window’. CC’d Philip for
clarification.

If you customize where you want it with ‘display-buffer-alist’ that
should be respected.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-indentation-in-lus-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 7431 bytes --]

From c32ce5aef2c8692cb93e6d91a7dc08af4b50c210 Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
Date: Sat, 23 Sep 2023 10:49:11 -0500
Subject: [PATCH] Improve indentation in lus-ts-mode (Bug#66159)

- Align "then" with if/elseif/else/end when it appears at the
  beginning of a line.

- Align a single-line of "end"s with the beginning of the
  outermost block being closed.

- Anchor indent of the first child in arguments, parameters and
  tables to the parent and the other children to the first.

* lisp/progmodes/lua-ts-mode.el (lua-ts--first-child)
(lua-ts--end-indent-offset)
(lua-ts--simple-indent-rules): Improve indentation.
* test/lisp/progmodes/lua-ts-mode-resources/indent.erts: Add
tests.
---
 lisp/progmodes/lua-ts-mode.el                 |  26 ++-
 .../lua-ts-mode-resources/indent.erts         | 179 ++++++++++++++++++
 2 files changed, 201 insertions(+), 4 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..fcab8ec610b 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -233,6 +233,17 @@ lua-ts--font-lock-settings
    '((ERROR) @font-lock-warning-face))
   "Tree-sitter font-lock settings for `lua-ts-mode'.")
 
+(defun lua-ts--first-child (node _p _b &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
+(defun lua-ts--end-indent-offset (_n _p _b &rest _)
+  "Calculate indent offset based on `end' count."
+  (let* ((beg (line-beginning-position))
+         (end (line-end-position))
+         (count (count-matches "end" beg end)))
+    (- (* (1- count) lua-ts-indent-offset))))
+
 (defvar lua-ts--simple-indent-rules
   `((lua
      ((parent-is "chunk") column-0 0)
@@ -240,9 +251,10 @@ lua-ts--simple-indent-rules
      ((parent-is "block") parent-bol 0)
      ((node-is "}") parent-bol 0)
      ((node-is ")") parent-bol 0)
+     ((node-is "then") standalone-parent 0)
      ((node-is "else_statement") parent-bol 0)
      ((node-is "elseif_statement") parent-bol 0)
-     ((node-is "end") parent-bol 0)
+     ((node-is "end") parent-bol lua-ts--end-indent-offset)
      ((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)
@@ -251,9 +263,15 @@ lua-ts--simple-indent-rules
      ((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)
+     ((and (parent-is "table_constructor") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") (nth-sibling 1) 0)
+     ((and (parent-is "arguments") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") (nth-sibling 1) 0)
+     ((and (parent-is "parameters") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") (nth-sibling 1) 0)
      ((parent-is "ERROR") no-indent 0))))
 
 (defvar lua-ts--syntax-table
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..b0cfe11ef6f 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -32,6 +32,22 @@ f({
 ;(function()
 return false
 )()
+
+function foo (e)
+    if e == nil
+      then return 1000
+    else return e
+    end
+end
+
+function foo (e)
+    if e == nil
+    then
+        return 1000
+    else
+        return e
+    end
+end
 =-=
 print(
   0,
@@ -57,6 +73,22 @@ f({
 ;(function()
   return false
 )()
+
+function foo (e)
+  if e == nil
+  then return 1000
+  else return e
+  end
+end
+
+function foo (e)
+  if e == nil
+  then
+    return 1000
+  else
+    return e
+  end
+end
 =-=-=
 
 Name: Argument Indent
@@ -77,6 +109,13 @@ cost = 2,
 length = 8,
 	parallelism = 4,
 })
+
+fn(1,
+2,
+     3)
+
+fn(1, 2,
+3)
 =-=
 function h(
   string,
@@ -93,6 +132,13 @@ local p = h(
     length = 8,
     parallelism = 4,
   })
+
+fn(1,
+   2,
+   3)
+
+fn(1, 2,
+   3)
 =-=-=
 
 Name: Continuation Indent
@@ -150,3 +196,136 @@ repeat
   z = z * 2
 until z > 12
 =-=-=
+
+Name: Parameter Indent
+
+=-=
+fn(a,
+b) end
+
+fn(a, b,
+c) end
+
+fn(
+a,
+b
+) end
+=-=
+fn(a,
+   b) end
+
+fn(a, b,
+   c) end
+
+fn(
+  a,
+  b
+) end
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Table Indent
+
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+        down={Corridoor=true},
+        left={Corridoor=true},
+        right={Corridoor=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+           down={Floor=true,Wall=true},
+           left={Floor=true,Wall=true},
+           right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+          down={Floor=true,Wall=true},
+          left={Floor=true,Wall=true},
+          right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+              down={Corridoor=true},
+              left={Corridoor=true},
+              right={Corridoor=true}}
+}
+
+local Other = {
+    a = 1,
+    b = 2,
+    c = 3,
+}
+=-=-=
+
+Name: Single Line End
+
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+                end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+            end end end
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+    end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+end end end
+=-=-=
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-24 18:20     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-26 19:21       ` Andrey Listopadov
  2023-09-27  1:18         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-30  7:52       ` Philip Kaludercic
  1 sibling, 1 reply; 25+ messages in thread
From: Andrey Listopadov @ 2023-09-26 19:21 UTC (permalink / raw)
  To: john muhl; +Cc: 66159, Philip Kaludercic, Eli Zaretskii


john muhl <jm@pub.pink> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> > Another thing that bothers me is that I prefer Gassanenko-style packing
>>> > of `end' keywords so that they vertically align with the scope of the
>>> > opened block, as it saves so much vertical space and is easier for me to
>>> > read, but lua-ts-mode moves it to the latest innermost indentation
>>> > level, as opposed to the outermost depending on the count of ends in the
>>> > line itself:
>>>
>>> I don't see any reason not to support that style but I'm not sure how to
>>> do it. A patch would be welcome but I'll try to figure it out sometime.
>>
>> Maybe introduce indentation styles into lua-ts-mode, like CC Mode and
>> c-ts-mode have?
>
> I’ll have a look at what the c-ts-mode styles do and see what might be
> applicable. In this case the changes can be accommodated by default.

In my opinion, this isn't distinct enough to introduce a new style.
But it's up to you to decide, of course - I'm all for better editing
experience for Lua!

>> A far as I understand it, in the `lua-mode' the overall line indentation
>> is computed via subtracting indentation for every `end' in that line,
>> e.g. `end end end' subtracts `lua-indent-level three times from current
>> indent level.
>
> Thanks for the explanation. The attached patch should make end packing
> work now.

I've tried your latest patch and indeed `end' now pack themselves as in
the lua-mode.  Thank you very much!

>>> Sure. It's a new mode so nothing is really set in stone. Let me know if
>>> you have other suggestions.

I noticed that the `do' keyword is indented similarly to `then' before
the patch when put on the new line:

for i=1,10
    do
    print(i)
end

I'm not sure if that's a proper way to indent it or not though, but `do'
usually signifies start of the scope, so perhaps it shouldn't be
indented in this case.

There are also some weirdness in semantic navigation, but it's more
likely that I'm just not used to new ts-backed navigation yet.

Thanks for your work on lua-ts-mode, it's much more snappy editing
experience now!

If you're willing to dig into some (pretty crazy) involved examples, I
can send here some really convoluted nested anonymous functions that
currently are indented in a weird way in both modes. Neither does it
exactly right in my opiion, but I also don't know if there is the right
way to indent this.  I can send these examples later this week once I
finish an article I'm working on rightnow.

--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-26 19:21       ` Andrey Listopadov
@ 2023-09-27  1:18         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-30  9:59           ` Andrey Listopadov
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-27  1:18 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159, Philip Kaludercic, Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 3035 bytes --]

Andrey Listopadov <andreyorst@gmail.com> writes:

> john muhl <jm@pub.pink> writes:
>
>> Eli Zaretskii <eliz@gnu.org> writes:
>>
>>>> > Another thing that bothers me is that I prefer Gassanenko-style packing
>>>> > of `end' keywords so that they vertically align with the scope of the
>>>> > opened block, as it saves so much vertical space and is easier for me to
>>>> > read, but lua-ts-mode moves it to the latest innermost indentation
>>>> > level, as opposed to the outermost depending on the count of ends in the
>>>> > line itself:
>>>>
>>>> I don't see any reason not to support that style but I'm not sure how to
>>>> do it. A patch would be welcome but I'll try to figure it out sometime.
>>>
>>> Maybe introduce indentation styles into lua-ts-mode, like CC Mode and
>>> c-ts-mode have?
>>
>> I’ll have a look at what the c-ts-mode styles do and see what might be
>> applicable. In this case the changes can be accommodated by default.
>
> In my opinion, this isn't distinct enough to introduce a new style.
> But it's up to you to decide, of course - I'm all for better editing
> experience for Lua!

I agree there’s no immediate need.

Could you explain what the lua-indent-* options do in lua-mode? I tried
toggling them but didn’t see any difference between on/off.

>>> A far as I understand it, in the `lua-mode' the overall line indentation
>>> is computed via subtracting indentation for every `end' in that line,
>>> e.g. `end end end' subtracts `lua-indent-level three times from current
>>> indent level.
>>
>> Thanks for the explanation. The attached patch should make end packing
>> work now.
>
> I've tried your latest patch and indeed `end' now pack themselves as in
> the lua-mode.  Thank you very much!
>
>>>> Sure. It's a new mode so nothing is really set in stone. Let me know if
>>>> you have other suggestions.
>
> I noticed that the `do' keyword is indented similarly to `then' before
> the patch when put on the new line:
>
> for i=1,10
>     do
>     print(i)
> end
>
> I'm not sure if that's a proper way to indent it or not though, but `do'
> usually signifies start of the scope, so perhaps it shouldn't be
> indented in this case.

Fixed in the attached.

> There are also some weirdness in semantic navigation, but it's more
> likely that I'm just not used to new ts-backed navigation yet.

I’m sure there is room for improvement here too. Suggestions welcome.

> Thanks for your work on lua-ts-mode, it's much more snappy editing
> experience now!

Glad to hear it.

> If you're willing to dig into some (pretty crazy) involved examples, I
> can send here some really convoluted nested anonymous functions that
> currently are indented in a weird way in both modes. Neither does it
> exactly right in my opiion, but I also don't know if there is the right
> way to indent this.  I can send these examples later this week once I
> finish an article I'm working on rightnow.

Sure. Whenever you have the time.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Improve-indentation-in-lus-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 7874 bytes --]

From 5d41635442d2c1032d015d90527bc5bf6113d291 Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
Date: Sat, 23 Sep 2023 10:49:11 -0500
Subject: [PATCH] Improve indentation in lus-ts-mode (Bug#66159)

- Align lines starting with "then" or "do" to the same level as
  the corresponding "if" or "for" statement.

- Align a single-line of "end"s with the beginning of the
  outermost block being closed.

- Anchor indent of the first child in arguments, parameters and
  tables to the parent and the other children to the first.

* lisp/progmodes/lua-ts-mode.el (lua-ts--first-child)
(lua-ts--end-indent-offset)
(lua-ts--simple-indent-rules): Improve indentation.
* test/lisp/progmodes/lua-ts-mode-resources/indent.erts: Add
tests.
---
 lisp/progmodes/lua-ts-mode.el                 |  27 ++-
 .../lua-ts-mode-resources/indent.erts         | 199 ++++++++++++++++++
 2 files changed, 222 insertions(+), 4 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..418d53e0578 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -233,6 +233,17 @@ lua-ts--font-lock-settings
    '((ERROR) @font-lock-warning-face))
   "Tree-sitter font-lock settings for `lua-ts-mode'.")
 
+(defun lua-ts--first-child (node _p _b &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
+(defun lua-ts--end-indent-offset (_n _p _b &rest _)
+  "Calculate indent offset based on `end' count."
+  (let* ((beg (line-beginning-position))
+         (end (line-end-position))
+         (count (count-matches "end" beg end)))
+    (- (* (1- count) lua-ts-indent-offset))))
+
 (defvar lua-ts--simple-indent-rules
   `((lua
      ((parent-is "chunk") column-0 0)
@@ -240,9 +251,11 @@ lua-ts--simple-indent-rules
      ((parent-is "block") parent-bol 0)
      ((node-is "}") parent-bol 0)
      ((node-is ")") parent-bol 0)
+     ((node-is "do") standalone-parent 0)
+     ((node-is "then") standalone-parent 0)
      ((node-is "else_statement") parent-bol 0)
      ((node-is "elseif_statement") parent-bol 0)
-     ((node-is "end") parent-bol 0)
+     ((node-is "end") parent-bol lua-ts--end-indent-offset)
      ((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)
@@ -251,9 +264,15 @@ lua-ts--simple-indent-rules
      ((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)
+     ((and (parent-is "table_constructor") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") (nth-sibling 1) 0)
+     ((and (parent-is "arguments") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") (nth-sibling 1) 0)
+     ((and (parent-is "parameters") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") (nth-sibling 1) 0)
      ((parent-is "ERROR") no-indent 0))))
 
 (defvar lua-ts--syntax-table
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..a36a933e640 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -32,6 +32,22 @@ f({
 ;(function()
 return false
 )()
+
+function foo (e)
+    if e == nil
+      then return 1000
+    else return e
+    end
+end
+
+function foo (e)
+    if e == nil
+    then
+        return 1000
+    else
+        return e
+    end
+end
 =-=
 print(
   0,
@@ -57,6 +73,22 @@ f({
 ;(function()
   return false
 )()
+
+function foo (e)
+  if e == nil
+  then return 1000
+  else return e
+  end
+end
+
+function foo (e)
+  if e == nil
+  then
+    return 1000
+  else
+    return e
+  end
+end
 =-=-=
 
 Name: Argument Indent
@@ -77,6 +109,13 @@ cost = 2,
 length = 8,
 	parallelism = 4,
 })
+
+fn(1,
+2,
+     3)
+
+fn(1, 2,
+3)
 =-=
 function h(
   string,
@@ -93,6 +132,13 @@ local p = h(
     length = 8,
     parallelism = 4,
   })
+
+fn(1,
+   2,
+   3)
+
+fn(1, 2,
+   3)
 =-=-=
 
 Name: Continuation Indent
@@ -130,10 +176,20 @@ for k, v in pairs({}) do
 	 print(k, v)
 end
 
+for i=1,10
+    do
+    print(i)
+end
+
 while n < 10 do
 n = n + 1
 end
 
+while n < 10
+ do
+ n = n + 1
+end
+
 repeat
 z = z * 2
  until z > 12
@@ -142,11 +198,154 @@ for k, v in pairs({}) do
   print(k, v)
 end
 
+for i=1,10
+do
+  print(i)
+end
+
 while n < 10 do
   n = n + 1
 end
 
+while n < 10
+do
+  n = n + 1
+end
+
 repeat
   z = z * 2
 until z > 12
 =-=-=
+
+Name: Parameter Indent
+
+=-=
+fn(a,
+b) end
+
+fn(a, b,
+c) end
+
+fn(
+a,
+b
+) end
+=-=
+fn(a,
+   b) end
+
+fn(a, b,
+   c) end
+
+fn(
+  a,
+  b
+) end
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Table Indent
+
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+        down={Corridoor=true},
+        left={Corridoor=true},
+        right={Corridoor=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+           down={Floor=true,Wall=true},
+           left={Floor=true,Wall=true},
+           right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+          down={Floor=true,Wall=true},
+          left={Floor=true,Wall=true},
+          right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+              down={Corridoor=true},
+              left={Corridoor=true},
+              right={Corridoor=true}}
+}
+
+local Other = {
+    a = 1,
+    b = 2,
+    c = 3,
+}
+=-=-=
+
+Name: Single Line End
+
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+                end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+            end end end
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+    end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+end end end
+=-=-=
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-24 18:20     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-09-26 19:21       ` Andrey Listopadov
@ 2023-09-30  7:52       ` Philip Kaludercic
  1 sibling, 0 replies; 25+ messages in thread
From: Philip Kaludercic @ 2023-09-30  7:52 UTC (permalink / raw)
  To: john muhl; +Cc: Andrey Listopadov, 66159, Eli Zaretskii

john muhl <jm@pub.pink> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> > Another thing that bothers me is that I prefer Gassanenko-style packing
>>> > of `end' keywords so that they vertically align with the scope of the
>>> > opened block, as it saves so much vertical space and is easier for me to
>>> > read, but lua-ts-mode moves it to the latest innermost indentation
>>> > level, as opposed to the outermost depending on the count of ends in the
>>> > line itself:
>>> 
>>> I don't see any reason not to support that style but I'm not sure how to
>>> do it. A patch would be welcome but I'll try to figure it out sometime.
>>
>> Maybe introduce indentation styles into lua-ts-mode, like CC Mode and
>> c-ts-mode have?
>
> I’ll have a look at what the c-ts-mode styles do and see what might be
> applicable. In this case the changes can be accommodated by default.
>
> Andrey Listopadov <andreyorst@gmail.com> writes:
>
>> Thanks! The patch seems to work as I would expect for most cases.
>>
>>> I don't see any reason not to support that style but I'm not sure how to
>>> do it. A patch would be welcome but I'll try to figure it out sometime.
>>
>> A far as I understand it, in the `lua-mode' the overall line indentation
>> is computed via subtracting indentation for every `end' in that line,
>> e.g. `end end end' subtracts `lua-indent-level three times from current
>> indent level.
>
> Thanks for the explanation. The attached patch should make end packing
> work now.
>
>>> Sure. It's a new mode so nothing is really set in stone. Let me know if
>>> you have other suggestions.
>>
>> I also noticed that `lua-ts-inferior-lua` for some reason starts itself
>> in a new frame instead of just a new window. I haven't seen this
>> behavior with other inferior-*-modes, and the `lua-start-process'
>> function from `lua-mode' doesn't spawn a new frame either.
>>
>> Looking at the code, it seems that it was a deliberate choice, as I see
>> the use of `display-buffer-pop-up-frame' in the `display-buffer' ACTION
>> arg.
>
> It did use a window originally but I was asked to change it:
>
>>> +    (pop-to-buffer buffer)))
>>
>> I believe that `display-buffer-pop-up-frame' is the preferred alternative
>> to `pop-to-buffer' these days.
>
> Maybe I misunderstood and it was only about not using ‘pop-to-buffer’
> and should have been ‘display-buffer-pop-up-window’. CC’d Philip for
> clarification.

Yes, you are right, I meant `display-buffer-pop-up-window'.

> If you customize where you want it with ‘display-buffer-alist’ that
> should be respected.





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-27  1:18         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-09-30  9:59           ` Andrey Listopadov
  2023-09-30 13:57             ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 25+ messages in thread
From: Andrey Listopadov @ 2023-09-30  9:59 UTC (permalink / raw)
  To: john muhl; +Cc: 66159, Philip Kaludercic, Eli Zaretskii


john muhl <jm@pub.pink> writes:

> I agree there’s no immediate need.
>
> Could you explain what the lua-indent-* options do in lua-mode? I tried
> toggling them but didn’t see any difference between on/off.

I don't really know it myself, sorry. There seem to be some specifics
regarding how the content inside parenthesis is indented, but I'm not
using these either.

>> There are also some weirdness in semantic navigation, but it's more
>> likely that I'm just not used to new ts-backed navigation yet.
>
> I’m sure there is room for improvement here too. Suggestions welcome.

Here are a few examples. the █ represents the point:

█(function (x) return x + 1 end)(41)

Pressing C-M-f (forward-sexp) puts the point here:

 (function (x)█return x + 1 end)(41)

I would expect it to go over the whole parenthesized expression:

 (function (x) return x + 1 end)█(41)

Another weird thing here:

 function foo (x)
     if false then
         print "foo"
     elseif true then
         print "bar"
     end
 end█

Pressing C-M-b (backward-sexp) puts the point here:

 function foo (x)
    █if false then
         print "foo"
     elseif true then
         print "bar"
     end
 end

I would expect it to go before the `function' instead:

█function foo (x)
     if false then
         print "foo"
     elseif true then
         print "bar"
     end
 end

>> If you're willing to dig into some (pretty crazy) involved examples, I
>> can send here some really convoluted nested anonymous functions that
>> currently are indented in a weird way in both modes. Neither does it
>> exactly right in my opiion, but I also don't know if there is the right
>> way to indent this.  I can send these examples later this week once I
>> finish an article I'm working on rightnow.
>
> Sure. Whenever you have the time.

I've tried a couple of Lua formatters available, and as far as I can
see, most of them indent my code in a weird way too, so perhaps it's not
really a problem, although I'm not sure.

For example, here's a piece of code that implements an iterator wrapper
with anonymous functions only:

1  function partitioning (n, collect)
2      return function (iterator)
3          return function (...)
4              return (
5                  function (next1, ...)
6                      local count, arr, last = 0, {}
7                      return (function (f, ...)
8                          return (function (i, ...) return i(i, ...) end)(
9                              function (i, ...)
10                                 return f(function (x, ...)
11                                     return i(i, ...)(x, ...)
12                                 end, ...)
13                             end)
14                     end)(function (step)
15                         return function(iterable, ...)
16                             return (function (recur, iterable, ...)
17                                 if nil ~= ... then
18                                     if n-1 == count then
19                                         local arr1, count1 = arr, count
20                                         arr, count = {}, 0
21                                         return (function (...)
22                                             last = ...
23                                             return ...
24                                         end)(collect(arr1, count1+1, ...))
25                                     else
26                                         count = count + 1
27                                         return recur(iterable,
28                                                      (function (...)
29                                                          last = ...
30                                                          return ...
31                                                      end)(collect(arr, count, ...)))
32                                     end
33                                 elseif count ~= 0 then
34                                     count = 0
35                                     return last, arr
36                                 end
37                             end)(step, iterable, next1(iterable, ...))
38                         end
39                     end), ...
40                 end)(iterator(...))
41         end
42     end
43 end


Here are the lines I find strange.

First, the Immediately Invoked Function Expression (IIFE) indentation is
all over the place:

8                          return (function (i, ...) return i(i, ...) end)(
9                              function (i, ...)
10                                 return f(function (x, ...)
11                                     return i(i, ...)(x, ...)
12                                 end, ...)
13                             end)

The I would expect the inner IIFE to be indented like this:

8                          return (function (i, ...) return i(i, ...) end)(
9                              function (i, ...)
10                                 return f(function (x, ...)
11                                              return i(i, ...)(x, ...)
12                                          end, ...)
13                             end)


Another strange snippet:

21                                         return (function (...)
22                                             last = ...
23                                             return ...
24                                         end)(collect(arr1, count1+1, ...))

The `last' and `return' statements are not properly indented to the
function's scope. I expected it to be indented like this:

21                                         return (function (...)
22                                                     last = ...
23                                                     return ...
24                                                 end)(collect(arr1, count1+1, ...))

And, weirdly, enough, a bit further in the code there's a similar
expression, but it is indented correctly:

27                                         return recur(iterable,
28                                                      (function (...)
29                                                          last = ...
30                                                          return ...
31                                                      end)(collect(arr, count, ...)))

I know, that this code is convoluted, and probably no one writes like
this, it was done on purpose for an article about the power of anonymous
functions, so perhaps it isn't really a problem for most Lua
programmers. But it makes reading code a bit harder in rare cases like
this. I have more examples in the article, feel free to grab them if you
need code snippets for testing:

https://andreyor.st/posts/2023-09-26-iterator-based-transducers-in-lua/

Other than that, and the navigation issues I've mentioned above, the
mode works solidly. I've also noticed that some of the `lua-mode'
default shortcuts are missing, like `C-c C-f' (lua-search-documentation)
and `C-c C-l' (lua-send-buffer), but these may be out of the scope of
this module, so let me know if I should just define these myself.

Thanks!


--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-30  9:59           ` Andrey Listopadov
@ 2023-09-30 13:57             ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-03 15:04               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-09-30 13:57 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159, Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 2914 bytes --]

Andrey Listopadov <andreyorst@gmail.com> writes:

> john muhl <jm@pub.pink> writes:
>
>> I agree there’s no immediate need.
>>
>> Could you explain what the lua-indent-* options do in lua-mode? I tried
>> toggling them but didn’t see any difference between on/off.
>
> I don't really know it myself, sorry. There seem to be some specifics
> regarding how the content inside parenthesis is indented, but I'm not
> using these either.

I found <https://github.com/immerrr/lua-mode/pull/151> with the
explanation of those options.

>>> There are also some weirdness in semantic navigation, but it's more
>>> likely that I'm just not used to new ts-backed navigation yet.
>>
>> I’m sure there is room for improvement here too. Suggestions welcome.
>
> Here are a few examples. the █ represents the point:

Fixed in the attached.

>>> If you're willing to dig into some (pretty crazy) involved examples, I
>>> can send here some really convoluted nested anonymous functions that
>>> currently are indented in a weird way in both modes. Neither does it
>>> exactly right in my opiion, but I also don't know if there is the right
>>> way to indent this.  I can send these examples later this week once I
>>> finish an article I'm working on rightnow.
>>
>> Sure. Whenever you have the time.
>
> I know, that this code is convoluted, and probably no one writes like
> this, it was done on purpose for an article about the power of anonymous
> functions, so perhaps it isn't really a problem for most Lua
> programmers. But it makes reading code a bit harder in rare cases like
> this. I have more examples in the article, feel free to grab them if you
> need code snippets for testing:
>
> https://andreyor.st/posts/2023-09-26-iterator-based-transducers-in-lua/

Thanks. The patch gets pretty close on the partitioning function from
your article (the mapping, filtering and taking functions indent
correctly now). The trouble is that you end up with a tree like:

  (return_statement
   (expression_list
    (function_call
     (parenthesized_expression
      (function_definition
       (end))))))

with two different indent styles.

  return (function (f, ...)                  -- L7
  end)(function (step)                       -- L14

  return (function (...)                     -- L21
          end)(collect(arr1, count1+1, ...)) -- L24

Is there some pattern for deciding that I’m missing?

> Other than that, and the navigation issues I've mentioned above, the
> mode works solidly. I've also noticed that some of the `lua-mode'
> default shortcuts are missing, like `C-c C-f'
> (lua-search-documentation)

The opening a browser to look at the docs approach isn’t very Emacs-y so
didn’t make the cut. Ideally it should integrate with info and/or eldoc
but I haven’t gotten to it yet. This should get you by in the meantime:


[-- Attachment #2: lua-ts-documentation-at-point --]
[-- Type: text/plain, Size: 1193 bytes --]

(defun jm~lua-ts-documentation-at-point ()
  "Show documentation of function at point in the Lua manual."
  (interactive)
  (save-excursion
    ;; When point is mid-word `treesit-thing-at-point' may return
    ;; the parent node of the thing at point. Go to the beginning
    ;; of the line or word to make sure we have the right thing.
    (unless (or (bolp)
                (not (string-match-p "[[:alnum:]]"
                                     (char-to-string (char-before)))))
      (backward-word))
    (when-let* ((node (treesit-thing-at-point 'sentence nil))
                ((equal "function_call" (treesit-node-type node)))
                (child (treesit-node-child-by-field-name node "name"))
                (name (treesit-node-text child t))
                (id (if (string-match-p ":" name)
                        (replace-regexp-in-string "^.*:" "file:" name)
                      name))
                ((string-match-p (rx-to-string `(: (? "file:")
                                                   (or ,@lua-ts--builtins)))
                                 id)))
      (browse-url (concat "file:///usr/share/doc/lua/manual.html"
                          "#pdf-" id)))))

[-- Attachment #3: Type: text/plain, Size: 434 bytes --]


> and `C-c C-l' (lua-send-buffer), but these may be out of the scope of
> this module, so let me know if I should just define these myself.

The attached patch includes lua-ts-send-{buffer,file,region}
commands with bindings for:

  C-c C-n start inferior Lua
  C-c C-c send buffer
  C-c C-l send file
  C-c C-r send region

I’ve been using a send-thing but wasn’t sure it’s worth including. What
do you think?


[-- Attachment #4: lua-ts-send-thing --]
[-- Type: text/plain, Size: 928 bytes --]

(defun jm~lua-ts-send-thing (point)
  "Send thing near POINT to the inferior Lua process."
  (interactive "d")
  (when-let* ((node (treesit-node-at point))
              (parent (treesit-parent-until
                       node
                       (lambda (p)
                         (string-match-p (rx (or "do_statement"
                                                 "for_statement"
                                                 "function_call"
                                                 "function_declaration"
                                                 "if_statement"
                                                 "repeat_statement"
                                                 "while_statement"))
                                         (treesit-node-type p)))
                       t)))
    (lua-ts-send-region (treesit-node-start parent)
                        (treesit-node-end parent))))

[-- Attachment #5: Type: text/plain, Size: 258 bytes --]


It looks at the tree around point and tries to find something to eval;
e.g.

  function fun[1](n)
      if n > 1
      th[2]en print(1)
      else p[3]rint(0)
      end
  end

Point at [1] sends the function, [2] the if statement, [3] the print
statement.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #6: 0001-Various-improvements-in-lus-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 18355 bytes --]

From f746dc8bcbe70cccb6d810cfa0e6480d7d6a752a Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
Date: Sat, 23 Sep 2023 10:49:11 -0500
Subject: [PATCH] Various improvements in lus-ts-mode (Bug#66159)

* lisp/progmodes/lua-ts-mode.el (lua-ts--first-child)
(lua-ts--end-indent-offset)
(lua-ts--simple-indent-rules): Improve indentation.
(lua-ts-inferior--write-history)
(lua-ts-inferior-lua): Improve inferior Lua interaction.
(lua-ts-mode-map): Add keymap.
(lua-ts-mode-menu): Add menu.
(lua-ts-mode): Add mode map and improve sexp navigation.
(lua-ts-send-buffer)
(lua-ts-send-file)
(lua-ts-send-line)
(lua-ts-send-region)
(lua-ts-show-process-buffer)
(lua-ts-hide-process-buffer)
(lua-ts-kill-process): New functions.
(lua-ts-mode-hook)
(lua-ts-inferior-prompt-regexp)
(lua-ts-inferior-prompt)
(lua-ts-inferior-prompt-continue)
(lua-ts-inferior-history): New variables.
---
 lisp/progmodes/lua-ts-mode.el                 | 207 ++++++++++++++++--
 .../lua-ts-mode-resources/indent.erts         | 199 +++++++++++++++++
 .../lua-ts-mode-resources/movement.erts       |  56 +++++
 3 files changed, 438 insertions(+), 24 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..f3fb48ee18a 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -48,6 +48,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
@@ -86,9 +95,22 @@ lua-ts-inferior-startfile
   :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-prompt ">"
+  "The prompt of the inferior Lua process."
+  :type 'string
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-prompt-continue ">>"
+  "The continuation prompt of the inferior Lua process."
+  :type 'string
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-history nil
+  "File used to save command history of the inferior Lua process."
+  :type '(choice (const nil) file)
+  :safe 'string-or-null-p
   :group 'lua-ts
   :version "30.1")
 
@@ -233,27 +255,65 @@ lua-ts--font-lock-settings
    '((ERROR) @font-lock-warning-face))
   "Tree-sitter font-lock settings for `lua-ts-mode'.")
 
+(defun lua-ts--first-child (node _p _b &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
+(defun lua-ts--end-indent-offset (_n _p _b &rest _)
+  "Calculate indent offset based on `end' count."
+  (let* ((beg (line-beginning-position))
+         (end (line-end-position))
+         (count (count-matches "end" beg end)))
+    (- (* (1- count) lua-ts-indent-offset))))
+
 (defvar lua-ts--simple-indent-rules
   `((lua
+     ((n-p-gp "end" "function_definition" "arguments")
+      parent 0)
+     ((n-p-gp "end" "function_definition" "parenthesized_expression")
+      parent 0)
+     ((and (n-p-gp "block" "function_definition" "arguments")
+           (lambda (_n parent &rest _)
+             (save-excursion
+               (goto-char (treesit-node-start parent))
+               (backward-char 2)
+               (not (looking-at ")")))))
+      parent lua-ts-indent-offset)
+     ((and (n-p-gp "block" "function_definition" "parenthesized_expression")
+           (lambda (node _p bol &rest _)
+             (equal "assignment_statement"
+                    (treesit-node-type
+                     (treesit-node-first-child-for-pos node bol)))))
+      parent lua-ts-indent-offset)
      ((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 "do") standalone-parent 0)
+     ((node-is "then") standalone-parent 0)
      ((node-is "else_statement") parent-bol 0)
      ((node-is "elseif_statement") parent-bol 0)
-     ((node-is "end") parent-bol 0)
+     ((node-is "end") parent-bol lua-ts--end-indent-offset)
      ((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 "function_definition") standalone-parent lua-ts-indent-offset)
+     ((parent-is "parenthesized_expression")
+      standalone-parent 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)
+     ((and (parent-is "table_constructor") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") (nth-sibling 1) 0)
+     ((and (parent-is "arguments") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") (nth-sibling 1) 0)
+     ((and (parent-is "parameters") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") (nth-sibling 1) 0)
      ((parent-is "ERROR") no-indent 0))))
 
 (defvar lua-ts--syntax-table
@@ -348,30 +408,127 @@ lua-ts-flymake-luacheck
       (process-send-region lua-ts--flymake-process (point-min) (point-max))
       (process-send-eof lua-ts--flymake-process))))
 
+(defun lua-ts-inferior--write-history (process _event)
+  "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 buffer)))
+    (with-current-buffer buffer (comint-write-input-ring))))
+
 ;;;###autoload
 (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))
-      (setq-local comint-input-ignoredups 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))))))
+  (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 comint-prompt-regexp (rx-to-string `(: bol
+                                                   ,lua-ts-inferior-prompt
+                                                   space)))
+      (setq-local comint-input-ignoredups t)
+      (setq-local comint-input-ring-file-name lua-ts-inferior-history)
+      (setq-local comint-prompt-read-only t)
+      (setq-local comint-use-prompt-regexp t)
+      (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)
+                                                       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-window)
+                                   (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)))
+
+(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)
+    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)
@@ -415,7 +572,9 @@ lua-ts-mode
                                       "while_statement")))
                    (sexp ,(rx (or "arguments"
                                   "block"
+                                  "function_declaration"
                                   "parameters"
+                                  "parenthesized_expression"
                                   "string"
                                   "table_constructor")))
                    (text "comment"))))
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..c407d3400f8 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -32,6 +32,22 @@ f({
 ;(function()
 return false
 )()
+
+function foo (e)
+    if e == nil
+      then return 1000
+    else return e
+    end
+end
+
+function foo (e)
+    if e == nil
+    then
+        return 1000
+    else
+        return e
+    end
+end
 =-=
 print(
   0,
@@ -57,6 +73,22 @@ f({
 ;(function()
   return false
 )()
+
+function foo (e)
+  if e == nil
+  then return 1000
+  else return e
+  end
+end
+
+function foo (e)
+  if e == nil
+  then
+    return 1000
+  else
+    return e
+  end
+end
 =-=-=
 
 Name: Argument Indent
@@ -77,6 +109,13 @@ cost = 2,
 length = 8,
 	parallelism = 4,
 })
+
+fn(1,
+2,
+     3)
+
+fn(1, 2,
+3)
 =-=
 function h(
   string,
@@ -93,6 +132,13 @@ local p = h(
     length = 8,
     parallelism = 4,
   })
+
+fn(1,
+   2,
+   3)
+
+fn(1, 2,
+   3)
 =-=-=
 
 Name: Continuation Indent
@@ -130,10 +176,20 @@ for k, v in pairs({}) do
 	 print(k, v)
 end
 
+for i=1,10
+    do
+    print(i)
+end
+
 while n < 10 do
 n = n + 1
 end
 
+while n < 10
+ do
+ n = n + 1
+end
+
 repeat
 z = z * 2
  until z > 12
@@ -142,11 +198,154 @@ for k, v in pairs({}) do
   print(k, v)
 end
 
+for i=1,10
+do
+  print(i)
+end
+
 while n < 10 do
   n = n + 1
 end
 
+while n < 10
+do
+  n = n + 1
+end
+
 repeat
   z = z * 2
 until z > 12
 =-=-=
+
+Name: Parameter Indent
+
+=-=
+fn(a,
+b)
+
+fn(a, b,
+c)
+
+fn(
+a,
+b
+)
+=-=
+fn(a,
+   b)
+
+fn(a, b,
+   c)
+
+fn(
+  a,
+  b
+)
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Table Indent
+
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+        down={Corridoor=true},
+        left={Corridoor=true},
+        right={Corridoor=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+           down={Floor=true,Wall=true},
+           left={Floor=true,Wall=true},
+           right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+          down={Floor=true,Wall=true},
+          left={Floor=true,Wall=true},
+          right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+              down={Corridoor=true},
+              left={Corridoor=true},
+              right={Corridoor=true}}
+}
+
+local Other = {
+    a = 1,
+    b = 2,
+    c = 3,
+}
+=-=-=
+
+Name: Single Line End
+
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+                end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+            end end end
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+    end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+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..afebe93de3f 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
@@ -481,6 +481,34 @@ local t = { 1,
   3 }|
 =-=-=
 
+Name: forward-sexp moves over parenthesized expressions
+
+=-=
+|(function (x) return x + 1 end)(41)
+=-=
+(function (x) return x + 1 end)|(41)
+=-=-=
+
+Name: forward-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|
+=-=-=
+
 Code:
   (lambda ()
     (lua-ts-mode)
@@ -551,3 +579,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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-09-30 13:57             ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-03 15:04               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-03 19:13                 ` Andrey Listopadov
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-03 15:04 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159, Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 92 bytes --]

Whoops. Looks like I forgot to update the patch before sending. This one
should be better.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Various-improvements-in-lus-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 18788 bytes --]

From 4ee42d82557e188537693c3a27b2246ed690c2a6 Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
Date: Sat, 23 Sep 2023 10:49:11 -0500
Subject: [PATCH] Various improvements in lus-ts-mode (Bug#66159)

* lisp/progmodes/lua-ts-mode.el (lua-ts--first-child)
(lua-ts--end-indent-offset)
(lua-ts--simple-indent-rules): Improve indentation.
(lua-ts-inferior--write-history)
(lua-ts-inferior-lua): Improve inferior Lua interaction.
(lua-ts-mode-map): Add keymap.
(lua-ts-mode-menu): Add menu.
(lua-ts-mode): Add mode map and improve sexp navigation.
(lua-ts-send-buffer)
(lua-ts-send-file)
(lua-ts-send-line)
(lua-ts-send-region)
(lua-ts-show-process-buffer)
(lua-ts-hide-process-buffer)
(lua-ts-kill-process): New functions.
(lua-ts-mode-hook)
(lua-ts-inferior-prompt-regexp)
(lua-ts-inferior-prompt)
(lua-ts-inferior-prompt-continue)
(lua-ts-inferior-history): New variables.
---
 lisp/progmodes/lua-ts-mode.el                 | 210 ++++++++++++++++--
 .../lua-ts-mode-resources/indent.erts         | 199 +++++++++++++++++
 .../lua-ts-mode-resources/movement.erts       |  56 +++++
 3 files changed, 441 insertions(+), 24 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..d1930d9f2b1 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -39,7 +39,9 @@
   (require 'rx))
 
 (declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-first-child-for-pos "treesit.c")
 (declare-function treesit-node-type "treesit.c")
+(declare-function treesit-node-start "treesit.c")
 (declare-function treesit-parser-create "treesit.c")
 (declare-function treesit-search-subtree "treesit.c")
 
@@ -48,6 +50,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
@@ -86,9 +97,22 @@ lua-ts-inferior-startfile
   :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-prompt ">"
+  "The prompt of the inferior Lua process."
+  :type 'string
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-prompt-continue ">>"
+  "The continuation prompt of the inferior Lua process."
+  :type 'string
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-history nil
+  "File used to save command history of the inferior Lua process."
+  :type '(choice (const nil) file)
+  :safe 'string-or-null-p
   :group 'lua-ts
   :version "30.1")
 
@@ -233,27 +257,65 @@ lua-ts--font-lock-settings
    '((ERROR) @font-lock-warning-face))
   "Tree-sitter font-lock settings for `lua-ts-mode'.")
 
+(defun lua-ts--first-child (node _p _b &rest _)
+  "Matches if NODE is the first among its siblings."
+  (= (treesit-node-index node) 1))
+
+(defun lua-ts--end-indent-offset (_n _p _b &rest _)
+  "Calculate indent offset based on `end' count."
+  (let* ((beg (line-beginning-position))
+         (end (line-end-position))
+         (count (count-matches "end" beg end)))
+    (- (* (1- count) lua-ts-indent-offset))))
+
 (defvar lua-ts--simple-indent-rules
   `((lua
+     ((n-p-gp "end" "function_definition" "arguments")
+      parent 0)
+     ((n-p-gp "end" "function_definition" "parenthesized_expression")
+      parent 0)
+     ((and (n-p-gp "block" "function_definition" "arguments")
+           (lambda (_n parent &rest _)
+             (save-excursion
+               (goto-char (treesit-node-start parent))
+               (backward-char 2)
+               (not (looking-at ")")))))
+      parent lua-ts-indent-offset)
+     ((and (n-p-gp "block" "function_definition" "parenthesized_expression")
+           (lambda (node _p bol &rest _)
+             (equal "assignment_statement"
+                    (treesit-node-type
+                     (treesit-node-first-child-for-pos node bol)))))
+      parent lua-ts-indent-offset)
      ((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 "do") standalone-parent 0)
+     ((node-is "then") standalone-parent 0)
      ((node-is "else_statement") parent-bol 0)
      ((node-is "elseif_statement") parent-bol 0)
-     ((node-is "end") parent-bol 0)
+     ((node-is "end") parent-bol lua-ts--end-indent-offset)
      ((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 "function_definition") standalone-parent lua-ts-indent-offset)
+     ((parent-is "parenthesized_expression")
+      standalone-parent 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)
+     ((and (parent-is "table_constructor") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") (nth-sibling 1) 0)
+     ((and (parent-is "arguments") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") (nth-sibling 1) 0)
+     ((and (parent-is "parameters") lua-ts--first-child)
+      parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") (nth-sibling 1) 0)
      ((parent-is "ERROR") no-indent 0))))
 
 (defvar lua-ts--syntax-table
@@ -348,30 +410,128 @@ lua-ts-flymake-luacheck
       (process-send-region lua-ts--flymake-process (point-min) (point-max))
       (process-send-eof lua-ts--flymake-process))))
 
+(defun lua-ts-inferior--write-history (process _event)
+  "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 buffer)))
+    (with-current-buffer buffer (comint-write-input-ring))))
+
 ;;;###autoload
 (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))
-      (setq-local comint-input-ignoredups 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))))))
+  (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 comint-prompt-regexp (rx-to-string `(: bol
+                                                   ,lua-ts-inferior-prompt
+                                                   space)))
+      (setq-local comint-input-ignoredups t)
+      (setq-local comint-input-ring-file-name lua-ts-inferior-history)
+      (setq-local comint-prompt-read-only t)
+      (setq-local comint-use-prompt-regexp t)
+      (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)
+                                                       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-window)
+                                   (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)))
+
+(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)
@@ -415,7 +575,9 @@ lua-ts-mode
                                       "while_statement")))
                    (sexp ,(rx (or "arguments"
                                   "block"
+                                  "function_declaration"
                                   "parameters"
+                                  "parenthesized_expression"
                                   "string"
                                   "table_constructor")))
                    (text "comment"))))
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..c407d3400f8 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -32,6 +32,22 @@ f({
 ;(function()
 return false
 )()
+
+function foo (e)
+    if e == nil
+      then return 1000
+    else return e
+    end
+end
+
+function foo (e)
+    if e == nil
+    then
+        return 1000
+    else
+        return e
+    end
+end
 =-=
 print(
   0,
@@ -57,6 +73,22 @@ f({
 ;(function()
   return false
 )()
+
+function foo (e)
+  if e == nil
+  then return 1000
+  else return e
+  end
+end
+
+function foo (e)
+  if e == nil
+  then
+    return 1000
+  else
+    return e
+  end
+end
 =-=-=
 
 Name: Argument Indent
@@ -77,6 +109,13 @@ cost = 2,
 length = 8,
 	parallelism = 4,
 })
+
+fn(1,
+2,
+     3)
+
+fn(1, 2,
+3)
 =-=
 function h(
   string,
@@ -93,6 +132,13 @@ local p = h(
     length = 8,
     parallelism = 4,
   })
+
+fn(1,
+   2,
+   3)
+
+fn(1, 2,
+   3)
 =-=-=
 
 Name: Continuation Indent
@@ -130,10 +176,20 @@ for k, v in pairs({}) do
 	 print(k, v)
 end
 
+for i=1,10
+    do
+    print(i)
+end
+
 while n < 10 do
 n = n + 1
 end
 
+while n < 10
+ do
+ n = n + 1
+end
+
 repeat
 z = z * 2
  until z > 12
@@ -142,11 +198,154 @@ for k, v in pairs({}) do
   print(k, v)
 end
 
+for i=1,10
+do
+  print(i)
+end
+
 while n < 10 do
   n = n + 1
 end
 
+while n < 10
+do
+  n = n + 1
+end
+
 repeat
   z = z * 2
 until z > 12
 =-=-=
+
+Name: Parameter Indent
+
+=-=
+fn(a,
+b)
+
+fn(a, b,
+c)
+
+fn(
+a,
+b
+)
+=-=
+fn(a,
+   b)
+
+fn(a, b,
+   c)
+
+fn(
+  a,
+  b
+)
+=-=-=
+
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 4)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Table Indent
+
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+        down={Floor=true,Wall=true},
+        left={Floor=true,Wall=true},
+        right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+        down={Corridoor=true},
+        left={Corridoor=true},
+        right={Corridoor=true}}
+}
+
+local Other = {
+a = 1,
+ b = 2,
+  c = 3,
+}
+=-=
+local Recipe = {
+    Floor={up={Floor=true,Wall=true},
+           down={Floor=true,Wall=true},
+           left={Floor=true,Wall=true},
+           right={Floor=true,Wall=true}},
+    Wall={up={Floor=true,Wall=true},
+          down={Floor=true,Wall=true},
+          left={Floor=true,Wall=true},
+          right={Floor=true,Wall=true}},
+    Corridor={up={Corridoor=true},
+              down={Corridoor=true},
+              left={Corridoor=true},
+              right={Corridoor=true}}
+}
+
+local Other = {
+    a = 1,
+    b = 2,
+    c = 3,
+}
+=-=-=
+
+Name: Single Line End
+
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+                end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+            end end end
+=-=
+function lowest_entropy_cell(world)
+    local lowest,res=math.huge,nil
+    for y=1,world.height do
+        for x=1,world.width do
+            local cell=world:get(x,y)
+            if cell.is_set then
+                local e=cell_enthropy(cell)
+                trace(e)
+                if e <= lowest then
+                    lowest,res=e,{x,y}
+    end end end end
+    return res or {math.random(w.w),math.random(w.h)}
+end
+
+for y=1,world.height do
+    for x=1,world.width do
+        local cell=world:get(x,y)
+        if cell.is_set then
+            local e=cell_enthropy(cell)
+            trace(e)
+            if e <= lowest then
+                lowest,res=e,{x,y}
+            end
+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..afebe93de3f 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
@@ -481,6 +481,34 @@ local t = { 1,
   3 }|
 =-=-=
 
+Name: forward-sexp moves over parenthesized expressions
+
+=-=
+|(function (x) return x + 1 end)(41)
+=-=
+(function (x) return x + 1 end)|(41)
+=-=-=
+
+Name: forward-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|
+=-=-=
+
 Code:
   (lambda ()
     (lua-ts-mode)
@@ -551,3 +579,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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: 30.0.50; lua-ts-mode semantic indentation problems
  2023-10-03 15:04               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-03 19:13                 ` Andrey Listopadov
  0 siblings, 0 replies; 25+ messages in thread
From: Andrey Listopadov @ 2023-10-03 19:13 UTC (permalink / raw)
  To: john muhl; +Cc: 66159, Eli Zaretskii


john muhl <jm@pub.pink> writes:

> Whoops. Looks like I forgot to update the patch before sending. This one
> should be better.
>
> [2. text/x-patch; 0001-Various-improvements-in-lus-ts-mode-Bug-66159.patch]...

Thanks! I tried the patch, and it works great! I noticed a small
indentation problem:

function (...)
    return (function (x)
        return x
            end)(foo(...))
end

probably this should be:

function (...)
    return (function (x)
                return x
            end)(...)
end

The movement now works great, thanks.

--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-09-22 19:17 bug#66159: 30.0.50; lua-ts-mode semantic indentation problems Andrey Listopadov
  2023-09-22 19:49 ` Eli Zaretskii
  2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-06 19:44 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-07 10:11   ` Mauro Aranda
  2023-10-07 16:15   ` Andrey Listopadov
  2 siblings, 2 replies; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-06 19:44 UTC (permalink / raw)
  To: 66159; +Cc: Andrey Listopadov

[-- Attachment #1: Type: text/plain, Size: 363 bytes --]

Tags: patch

Here is a cleaned up patch. A couple of small additions unrelated to
this bug are included but I can separate them if anyone prefers. The
additions are:

- Make lua-ts-mode-hook a defcustom.
- Add an option to use a history file for inferior process input.

Andrey if you could test this one it’d be appreciated. Thanks for your
help.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 28954 bytes --]

From 8f128eb7e68e0bafe0a123623e40cd59a062b93f Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
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                 | 296 +++++++-
 .../lua-ts-mode-resources/indent.erts         | 675 +++++++++++++++---
 .../lua-ts-mode-resources/movement.erts       |  56 ++
 3 files changed, 903 insertions(+), 124 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 030a3585158..8b4a60e74bf 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
@@ -86,9 +99,24 @@ lua-ts-inferior-startfile
   :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-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-history nil
+  "File used to save command history of the inferior Lua process."
+  :type '(choice (const nil) file)
+  :safe 'string-or-null-p
   :group 'lua-ts
   :version "30.1")
 
@@ -235,27 +263,125 @@ 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)
+     ((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--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 +478,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)
@@ -415,7 +639,9 @@ lua-ts-mode
                                       "while_statement")))
                    (sexp ,(rx (or "arguments"
                                   "block"
+                                  "function_declaration"
                                   "parameters"
+                                  "parenthesized_expression"
                                   "string"
                                   "table_constructor")))
                    (text "comment"))))
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
index 040225c8580..25d29e0fbc5 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -5,148 +5,645 @@ 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 f2(n)
+  print(n)
+  return n * 2
+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 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()
-  return false
-)()
+function f6(...)
+  local f = function (...)
+    if ok then
+      print(1)
+    else
+      print(0)
+    end
+  end
+  return f
+end
+
+;(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: 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
+=-=
+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
+=-=-=
+
+Name: Bracket Indent
+
+=-=
+fn(
+  )
+
+tb={
+   }
+=-=
+fn(
+)
+
+tb={
+}
 =-=-=
 
-Name: Continuation Indent
+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
 
 =-=
-for k, v in pairs({}) do
-	 print(k, v)
+ 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, })
+=-=
+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, })
+=-=-=
+
+Name: Parameter Indent
+
+=-=
+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..afebe93de3f 100644
--- a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
+++ b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
@@ -481,6 +481,34 @@ local t = { 1,
   3 }|
 =-=-=
 
+Name: forward-sexp moves over parenthesized expressions
+
+=-=
+|(function (x) return x + 1 end)(41)
+=-=
+(function (x) return x + 1 end)|(41)
+=-=-=
+
+Name: forward-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|
+=-=-=
+
 Code:
   (lambda ()
     (lua-ts-mode)
@@ -551,3 +579,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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-06 19:44 ` bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159) john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-07 10:11   ` Mauro Aranda
  2023-10-07 16:15   ` Andrey Listopadov
  1 sibling, 0 replies; 25+ messages in thread
From: Mauro Aranda @ 2023-10-07 10:11 UTC (permalink / raw)
  To: john muhl, 66159; +Cc: Andrey Listopadov

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.







^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-06 19:44 ` bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159) john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-07 10:11   ` Mauro Aranda
@ 2023-10-07 16:15   ` Andrey Listopadov
  2023-10-07 18:10     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  1 sibling, 1 reply; 25+ messages in thread
From: Andrey Listopadov @ 2023-10-07 16:15 UTC (permalink / raw)
  To: john muhl; +Cc: 66159


john muhl <jm@pub.pink> writes:

> Tags: patch
>
> Here is a cleaned up patch. A couple of small additions unrelated to
> this bug are included but I can separate them if anyone prefers. The
> additions are:
>
> - Make lua-ts-mode-hook a defcustom.
> - Add an option to use a history file for inferior process input.
>
> Andrey if you could test this one it’d be appreciated. Thanks for your
> help.
>
> [2. text/x-patch; 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch]...

It seems the last patch broke the movement.

--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-07 16:15   ` Andrey Listopadov
@ 2023-10-07 18:10     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-08  9:43       ` Andrey Listopadov
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-07 18:10 UTC (permalink / raw)
  To: Andrey Listopadov; +Cc: 66159

Andrey Listopadov <andreyorst@gmail.com> writes:

> john muhl <jm@pub.pink> writes:
>
>> Tags: patch
>>
>> Here is a cleaned up patch. A couple of small additions unrelated to
>> this bug are included but I can separate them if anyone prefers. The
>> additions are:
>>
>> - Make lua-ts-mode-hook a defcustom.
>> - Add an option to use a history file for inferior process input.
>>
>> Andrey if you could test this one it’d be appreciated. Thanks for your
>> help.
>>
>> [2. text/x-patch; 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch]...
>
> 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?





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-07 18:10     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-08  9:43       ` Andrey Listopadov
  2023-10-09  3:28         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 25+ messages in thread
From: Andrey Listopadov @ 2023-10-08  9:43 UTC (permalink / raw)
  To: john muhl; +Cc: 66159


john muhl <jm@pub.pink> 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


--
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-08  9:43       ` Andrey Listopadov
@ 2023-10-09  3:28         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-09  3:28 UTC (permalink / raw)
  To: 66159; +Cc: Andrey Listopadov, Mauro Aranda

[-- Attachment #1: Type: text/plain, Size: 2740 bytes --]

Mauro Aranda <maurooaranda@gmail.com> 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 <andreyorst@gmail.com> writes:

> john muhl <jm@pub.pink> 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.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 35241 bytes --]

From de03aed39d67f0a14b2586e59b2b713891bc37d0 Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-09  3:28         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-20 20:40             ` Stefan Kangas
                               ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-17  3:26 UTC (permalink / raw)
  To: 66159; +Cc: Andrey Listopadov, Mauro Aranda

[-- Attachment #1: Type: text/plain, Size: 3097 bytes --]

john muhl <jm@pub.pink> writes:

> Mauro Aranda <maurooaranda@gmail.com> 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 <andreyorst@gmail.com> writes:
>
>> john muhl <jm@pub.pink> 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.
>
> [2. text/x-patch; 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch]...

Here is the latest version. Fixed a missing declare-function
and other minor omissions.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Various-improvements-to-lua-ts-mode-Bug-66159.patch --]
[-- Type: text/x-patch, Size: 35728 bytes --]

From 8d3b9e3617364eb9e20d489bc3979405d50abdee Mon Sep 17 00:00:00 2001
From: john muhl <jm@pub.pink>
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): Improve movement.
(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                 | 382 ++++++++--
 .../lua-ts-mode-resources/indent.erts         | 705 +++++++++++++++---
 .../lua-ts-mode-resources/movement.erts       | 156 ++--
 3 files changed, 1045 insertions(+), 198 deletions(-)

diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
index 8db6816d6e4..2193779b759 100644
--- a/lisp/progmodes/lua-ts-mode.el
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -38,7 +38,12 @@
   (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-child-count "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 +53,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 +71,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 +84,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 +96,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 +132,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 +202,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 +268,145 @@ 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)
+     ((or (parent-is "function_declaration")
+          (parent-is "function_definition")
+          (parent-is "do_statement")
+          (parent-is "for_statement")
+          (parent-is "repeat_statement")
+          (parent-is "while_statement")
+          (parent-is "if_statement")
+          (parent-is "else_statement")
+          (parent-is "elseif_statement"))
+      standalone-parent lua-ts-indent-offset)
      ((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)
@@ -379,26 +530,126 @@ 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 (or (not (equal (buffer-name) lua-ts-inferior-buffer))
+                          (equal string
+                                 (concat lua-ts-inferior-prompt-continue " ")))
+                      string
+                    (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)
@@ -431,20 +682,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


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-20 20:40             ` Stefan Kangas
  2023-10-22 20:03               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-21  5:15             ` Andrey
  2023-10-21 11:37             ` Andrey
  2 siblings, 1 reply; 25+ messages in thread
From: Stefan Kangas @ 2023-10-20 20:40 UTC (permalink / raw)
  To: john muhl, 66159; +Cc: Andrey Listopadov, Mauro Aranda

john muhl via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs@gnu.org> writes:

> Here is the latest version. Fixed a missing declare-function
> and other minor omissions.

Thanks.  Is this ready for installing on master, or is there anything
else that needs doing first?





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-20 20:40             ` Stefan Kangas
@ 2023-10-21  5:15             ` Andrey
  2023-10-21 11:37             ` Andrey
  2 siblings, 0 replies; 25+ messages in thread
From: Andrey @ 2023-10-21  5:15 UTC (permalink / raw)
  To: john muhl, 66159; +Cc: Mauro Aranda


>Here is the latest version. Fixed a missing declare-function
>and other minor omissions.
>

Thanks! Unfortunately, I'm on the trip right now and can't check the patch.
Sorry for delay. The last patch was great, so I have no objections from my part.

-- 
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-20 20:40             ` Stefan Kangas
  2023-10-21  5:15             ` Andrey
@ 2023-10-21 11:37             ` Andrey
  2 siblings, 0 replies; 25+ messages in thread
From: Andrey @ 2023-10-21 11:37 UTC (permalink / raw)
  To: john muhl, 66159; +Cc: Mauro Aranda


>Here is the latest version. Fixed a missing declare-function
>and other minor omissions.
>

Thanks! Unfortunately, I'm on the trip right now and can't check the patch.
Sorry for delay. The last patch was great, so I have no objections from my part.

-- 
Andrey Listopadov
-- 
Andrey Listopadov





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-20 20:40             ` Stefan Kangas
@ 2023-10-22 20:03               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2023-10-23  8:11                 ` Stefan Kangas
  0 siblings, 1 reply; 25+ messages in thread
From: john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-10-22 20:03 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: 66159, Andrey Listopadov, Mauro Aranda

Stefan Kangas <stefankangas@gmail.com> writes:

> john muhl via "Bug reports for GNU Emacs, the Swiss army knife of text
> editors" <bug-gnu-emacs@gnu.org> writes:
>
>> Here is the latest version. Fixed a missing declare-function
>> and other minor omissions.
>
> Thanks.  Is this ready for installing on master, or is there anything
> else that needs doing first?

It should be ok to install.





^ permalink raw reply	[flat|nested] 25+ messages in thread

* bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159)
  2023-10-22 20:03               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2023-10-23  8:11                 ` Stefan Kangas
  0 siblings, 0 replies; 25+ messages in thread
From: Stefan Kangas @ 2023-10-23  8:11 UTC (permalink / raw)
  To: john muhl; +Cc: 66159-done, Andrey Listopadov, Mauro Aranda

Version: 30.1

john muhl <jm@pub.pink> writes:

> It should be ok to install.

Thanks, installed on master.  I'm consequently closing this bug.

[1: 1c261e0a6ca]: 2023-10-23 10:07:52 +0200
  Various improvements to lua-ts-mode (Bug#66159)
  https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=1c261e0a6cae09e3ff36930442f2c9da44bccd6d





^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2023-10-23  8:11 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-22 19:17 bug#66159: 30.0.50; lua-ts-mode semantic indentation problems Andrey Listopadov
2023-09-22 19:49 ` Eli Zaretskii
2023-09-24 15:06 ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-24 15:44   ` Eli Zaretskii
2023-09-24 16:38   ` Andrey Listopadov
2023-09-24 18:20     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-26 19:21       ` Andrey Listopadov
2023-09-27  1:18         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-09-30  9:59           ` Andrey Listopadov
2023-09-30 13:57             ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-03 15:04               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-03 19:13                 ` Andrey Listopadov
2023-09-30  7:52       ` Philip Kaludercic
2023-10-06 19:44 ` bug#66159: [PATCH] Various improvements to lua-ts-mode (Bug#66159) john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-07 10:11   ` Mauro Aranda
2023-10-07 16:15   ` Andrey Listopadov
2023-10-07 18:10     ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-08  9:43       ` Andrey Listopadov
2023-10-09  3:28         ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-17  3:26           ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-20 20:40             ` Stefan Kangas
2023-10-22 20:03               ` john muhl via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-10-23  8:11                 ` Stefan Kangas
2023-10-21  5:15             ` Andrey
2023-10-21 11:37             ` Andrey

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).