LuaLS / lua-language-server

A language server that offers Lua language support - programmed in Lua
https://luals.github.io
MIT License
3.33k stars 316 forks source link

`align_continuous_assign_statement` has very strange behavior when formatting the whole document. #2878

Closed 4lph4-Ph4un closed 1 month ago

4lph4-Ph4un commented 1 month ago

How are you using the lua-language-server?

Visual Studio Code Extension (sumneko.lua)

Which OS are you using?

Windows

What is the issue affecting?

Formatting

Expected Behaviour

Case 1:

Assuming I have a file like this and I run formatting on the whole file, with the intention to aling the variables double and wrapped:

---@diagnostic disable: unused-function
---@diagnostic disable: unused-local

-- NOTE: This file is dedicated for testing code formatting:

-- NOTE: Indentation rules:
-- - Indentation is done with spaces
-- - Indentation size is 2

-- NOTE: Basic indentation:
--
-- NOTE: Right:
---@param  n integer
---@return integer
local function foo(n)
  if n % 2 == 0 then
    return n + 1
  end
  return 2 * n
end

-- NOTE: Continuation indentation:
-- - Indentation size is 2

-- NOTE: Always indent by 2
-- and if we do this with "if"
-- close the group with "end"
-- on a new line:
if true and true
  or false and false
  or true
then
  print("Yay!")
end

-- NOTE: Quoting:
-- - String quoting is to be done with double-quotes
-- - String quoting is to be done with single-quotes if
--   double quotes are used inside the string.

local double = "foo"
local wrapped = '"bar"'

-- NOTE: Table-styling:
-- NOTE: Definitions:
-- - Table separator is colon
-- - Space around table field lists:
--   - "true" for map-style tables
--   - "false" for arrays
--   - The style-checker won't support this currently
-- - Trailing table separator should be used
--   on multiline tables and arrays

-- NOTE: The style-check can't handle so we use this:
-- The rule is on for both tables but since we rarely
-- define map-style tables as one-liners it's okay for now:
---@diagnostic disable-next-line: codestyle-check
local single_line_map = { foo = 1 }
local single_line_arr = {1, 2, 3}

-- NOTE: Trailing commas are OK:
local multiline_map = {
  foo = {
    bar = 1,
  },
  baz = 2,
}

local multiline_arr = {
  1,
  2,
}

-- NOTE: Accessor spacing:
-- - No spaces inside square-brackets
-- - No spaces between the "+" in table append operator
local x = single_line_arr[#single_line_arr]

single_line_arr[#single_line_arr + 1] = 4

-- NOTE: Table field alignment:
-- - The table fields are to be aligned by the equals sign
-- - Continuous arrays are to be aligned by items in the table
local aligned_map = {
  foo    = 1,
  foobar = 2,
}

local aligned_matrix = {
  {1,   12, 123, 321, 32, 1},
  {1,   2,  3,   4,   5},
  {123, 2},
}

-- NOTE: Semicolons:
-- - Allowed when multiple expressions are
--   on a single line:
local a = 1; a = a + 1; local b = a + 2

I'd assume everything else to stay the same, but the these two assignments to be aligned:

-- NOTE: Quoting:
-- - String quoting is to be done with double-quotes
-- - String quoting is to be done with single-quotes if
--   double quotes are used inside the string.

local double  = "foo"
local wrapped = '"bar"'

Case 2: Here's where things get REALLY strange!!!

Now if you start of in the state where those two variables are already aligned and run the formatter, you'd assume this code would stay the same:

---@diagnostic disable: unused-function
---@diagnostic disable: unused-local

-- NOTE: This file is dedicated for testing code formatting:

-- NOTE: Indentation rules:
-- - Indentation is done with spaces
-- - Indentation size is 2

-- NOTE: Basic indentation:
--
-- NOTE: Right:
---@param  n integer
---@return integer
local function foo(n)
  if n % 2 == 0 then
    return n + 1
  end
  return 2 * n
end

-- NOTE: Continuation indentation:
-- - Indentation size is 2

-- NOTE: Always indent by 2
-- and if we do this with "if"
-- close the group with "end"
-- on a new line:
if true and true
  or false and false
  or true
then
  print("Yay!")
end

-- NOTE: Quoting:
-- - String quoting is to be done with double-quotes
-- - String quoting is to be done with single-quotes if
--   double quotes are used inside the string.

local double  = "foo"
local wrapped = '"bar"'

-- NOTE: Table-styling:
-- NOTE: Definitions:
-- - Table separator is colon
-- - Space around table field lists:
--   - "true" for map-style tables
--   - "false" for arrays
--   - The style-checker won't support this currently
-- - Trailing table separator should be used
--   on multiline tables and arrays

-- NOTE: The style-check can't handle so we use this:
-- The rule is on for both tables but since we rarely
-- define map-style tables as one-liners it's okay for now:
---@diagnostic disable-next-line: codestyle-check
local single_line_map = {foo = 1}
local single_line_arr = {1, 2, 3}

-- NOTE: Trailing commas are OK:
local multiline_map = {
  foo = {
    bar = 1,
  },
  baz = 2,
}

local multiline_arr = {
  1,
  2,
}

-- NOTE: Accessor spacing:
-- - No spaces inside square-brackets
-- - No spaces between the "+" in table append operator
local x = single_line_arr[#single_line_arr]

single_line_arr[#single_line_arr + 1] = 4

-- NOTE: Table field alignment:
-- - The table fields are to be aligned by the equals sign
-- - Continuous arrays are to be aligned by items in the table
local aligned_map = {
  foo    = 1,
  foobar = 2,
}

local aligned_matrix = {
  {1,   12, 123, 321, 32, 1},
  {1,   2,  3,   4,   5},
  {123, 2},
}

-- NOTE: Semicolons:
-- - Allowed when multiple expressions are
--   on a single line:
local a = 1; a = a + 1; local b = a + 2

Actual Behaviour

Case 1: Nothing happens and the code stays the same.

Case 2: The formatter makes a total and complete mess of the code like (results below):

---@diagnostic disable: unused-function
---@diagnostic disable: unused-local

-- NOTE: This file is dedicated for testing code formatting:

-- NOTE: Indentation rules:
-- - Indentation is done with spaces
-- - Indentation size is 2

-- NOTE: Basic indentation:
--
-- NOTE: Right:
---@param  n integer
---@return integer
local function foo(n)
  if n % 2 == 0 then
    return n + 1
  end
  return 2 * n
end

-- NOTE: Continuation indentation:
-- - Indentation size is 2

-- NOTE: Always indent by 2
-- and if we do this with "if"
-- close the group with "end"
-- on a new line:
if true and true
  or false and false
  or true
then
  print("Yay!")
end

-- NOTE: Quoting:
-- - String quoting is to be done with double-quotes
-- - String quoting is to be done with single-quotes if
--   double quotes are used inside the string.

local double                          = "foo"
local wrapped                         = '"bar"'

-- NOTE: Table-styling:
-- NOTE: Definitions:
-- - Table separator is colon
-- - Space around table field lists:
--   - "true" for map-style tables
--   - "false" for arrays
--   - The style-checker won't support this currently
-- - Trailing table separator should be used
--   on multiline tables and arrays

-- NOTE: The style-check can't handle so we use this:
-- The rule is on for both tables but since we rarely
-- define map-style tables as one-liners it's okay for now:
---@diagnostic disable-next-line: codestyle-check
local single_line_map                 = {foo = 1}
local single_line_arr                 = {1, 2, 3}

-- NOTE: Trailing commas are OK:
local multiline_map                   = {
  foo = {
    bar = 1,
  },
  baz = 2,
}

local multiline_arr                   = {
  1,
  2,
}

-- NOTE: Accessor spacing:
-- - No spaces inside square-brackets
-- - No spaces between the "+" in table append operator
local x                               = single_line_arr[#single_line_arr]

single_line_arr[#single_line_arr + 1] = 4

-- NOTE: Table field alignment:
-- - The table fields are to be aligned by the equals sign
-- - Continuous arrays are to be aligned by items in the table
local aligned_map                     = {
  foo    = 1,
  foobar = 2,
}

local aligned_matrix                  = {
  {1,   12, 123, 321, 32, 1},
  {1,   2,  3,   4,   5},
  {123, 2},
}

-- NOTE: Semicolons:
-- - Allowed when multiple expressions are
--   on a single line:
local a                               = 1; a = a + 1; local b = a + 2

``

### Reproduction steps

1. Set the `.editorconfig` as I've described below.
2.  Write the code like in each of the cases.
3.  Run format for the whole document.
4.  Go into a fetal position.

### Additional Notes

Tested on both VSCode and Emacs:

My `.editorconfig`:

root = true

[*] charset = utf-8 indent_style = space end_of_line = lf insert_final_newline = true

[*.json] indent_size = 2

[{makefile,makefile.*}] indent_style = tab indent_size = 2

[*.lua] indent_size = 2 continuation_indent = 2 quote_style = double table_separator_style = colon trailing_table_separator = smart space_around_table_field_list = false align_continuous_assign_statement = true align_continuous_rect_table_field = true end_statement_with_semicolon = same_line



The way I see it, why would a formatter want to align every expression like that and what even is the logic behind this behavior? What even happens there? Why doesn't it align everything at all in the first case, and why on earth it aligns absolutely everything on the same file according to the longest assignment? Wouldn't a more reasonable default to align every assignment according the the closest longest alignment in the lines directly above or below the formatted lines and not according the whole file? Who even would want to make their code look like that? The way it works now, means a user can't ever define module-level variables and hoping to align them which makes the whole feature totally useless unless it's used inside functions.

### Log File

_No response_
CppCXY commented 1 month ago

set align_continuous_line_space = 1

4lph4-Ph4un commented 1 month ago

Thank you! That did the trick!