pkulchenko / ZeroBraneStudio

Lightweight Lua-based IDE for Lua with code completion, syntax highlighting, live coding, remote debugger, and code analyzer; supports Lua 5.1, 5.2, 5.3, 5.4, LuaJIT and other Lua interpreters on Windows, macOS, and Linux
http://studio.zerobrane.com/
Other
2.6k stars 519 forks source link

Too much indentation below anonymus function arguments #1099

Closed fbosio closed 3 years ago

fbosio commented 3 years ago

Hi! It's me again, the guy from #1068 :smile: here's the thing.

Description

According to the Programming in Lua book, function arguments of higher-order functions have below them the same indentation as regular function definitions.

names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function (n1, n2)
  return grades[n1] > grades[n2]    -- compare the grades
end)

But writing the third line and pressing Enter gives twice as the expected indentation level.

This seems to be a minor issue, but some modules like busted, the Lua unit testing module, rely heavily on higher-order functions and the extra indentation ends up being pretty annoying (at least to me) when writing code in ZeroBrane.

Location of the problem

I added this on line 123 of ./spec/lua.lua

ide:Print("opened " .. opened,
          "closed " .. closed,
          "func " .. func,
          "terminc " .. terminc,
          "ended " .. ended,
          "return " .. opened - closed + func + terminc - ended)

then, I ran zbstudio and wrote some lines of code (and obviously pressed the Enter key) in order to test indentation.

In the last case, the anonymus function argument is interpreted as a regular function definition, so func is set to 1 and isincindent returns an extra level of indentation.

Possible fix

isincindent could substract 1 from its output if there is an opening parenthesis to the left of the function keyword that is not closed at its right. In this particular case, I think that is better to do this for anonymus functions ending the line only, i.e., the function keyword followed by a pair of parenthesis and the end of the line.

I suggest to change line 123 of ./spec/lua.lua like this, because it worked for me:

local lineEndsWithFunctionArgument = str:match("function%s*%(%)%s*$")                           
                                     and str:gsub("%(%)", "")                                   
                                            :find("%([^%(]*function%s*$")                       
                                     and 1 or 0                                                 

return (opened - closed + func + terminc - ended                                                
        - lineEndsWithFunctionArgument)

Final comment

Happy Holidays! :grin: :christmas_tree: :tada: :confetti_ball:

pkulchenko commented 3 years ago

@fbosio, thank you for sharing your thoughts on this. The behavior is by design, as when an anonymous function is used as one of the parameters, it creates its own indentation. Consider the following code:

table.sort(names,
  function (n1, n2)
    print(1)
  end)

print(1) would require two indentations (one for being a (part of a) parameter in table.sort and one for being inside the anonymous function, so when the function definition is moved inline, it doesn't change the fact that print is still inside the function:

table.sort(names, function (n1, n2)
    print(1)
  end)

Also note that if there are other parameters after the function (not in the case of table.sort, but in other calls), the current formatting will still separate them from the function content (and this is why end is indented as well):

table.sort(names, function (n1, n2)
    print(1)
  end,
  2)

If you move the closing bracket to its own line, then it will be non-indented:

table.sort(names, function (n1, n2)
    print(1)
  end,
  2
)

I think this is a reasonable behavior and is also consistent with Function in tables.

I'm closing this, but am happy to continue the discussion...

fbosio commented 3 years ago

Thanks for your quick response!

I think this is a reasonable behavior and is also consistent with Function in tables.

I see, but... what about Spacing? I think it's exactly what I mean:

-- good
local my_table = {
   "hello",
   "world",
}
using_a_callback(x, function(...)
   print("hello")
end)

I'm closing this, but am happy to continue the discussion...

Great! :smile: It's just a matter of taste anyway...

pkulchenko commented 3 years ago

I agree, it does look inconsistent given their example in Spacing (as you noted). I still think that the indentation in the IDE is straightforward, as it only follows simple rules: each table definition, function call, function definition, or control structure creates a new level. When several of these happen on the same line, it creates several levels:

print(1, {a = function()
      print(2)
    end})

In this case print(2) will be indented exactly the same way as if all the parameters were on separate lines:

print(1,
  {
    a = function()
      print(2)
    end
  }
)

It is indeed a matter of taste, but I'm hoping that the rules are consistent (as currently applied) and produce expected results.