Subject: Re: bug#66159: 30.0.50; lua-ts-mode semantic indentation problems

From: Andrey Listopadov <[email-masked]>
Date: Sat, 30 Sep 2023 12:59:28 +0300

john muhl <[email-masked]> 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