johnnymorganz / stylua Goto Github PK
View Code? Open in Web Editor NEWAn opinionated Lua code formatter
License: Mozilla Public License 2.0
An opinionated Lua code formatter
License: Mozilla Public License 2.0
local val = 1 + 1 -- add
local val = 1
+ 1 -- add
local val = 1 + 1 -- add
Happens against the latest release and also a local build off master
With this code in a file:
function serializeInt(outputValue): number
end
I get this error:
error: could not format file scalars.lua: error parsing: error occurred while creating ast: unexpected token `:`. (starting from line 28, character 35 and ending on line 28, character 36)
additional information: expected 'end'
The indentation level of 'then' in multi-line if statements appears to decrease by one for every expanded function call it's nested in.
baz(
first_arg___ooooooooooooooooooooooooooooooooooooooooooo,
second_arg___qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq,
function()
if
multiline_if___aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
and multiline_if___bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
then
biz()
end
end
)
biz(
first_arg___aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
second_arg___bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb,
baz(
first_arg___ooooooooooooooooooooooooooooooooooooooooooo,
second_arg___qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq,
function()
if
multiline_if___aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
and multiline_if___bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
then
biz()
end
end
)
)
This exact problem does not appear to exist for expanded expressions that aren't function calls:
local result = first_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
and (function()
if
multiline_if___aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
and multiline_if___bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
then
end
return second
end)()
if someReallyLongCondition and someOtherReallyLongCondition and somethingElse then
doSomething()
doSomethingElse()
end
... should be formatted as ...
if
someReallyLongCondition
and someOtherReallyLongCondition
and somethingElse
then
doSomething()
doSomethingElse()
end
I had a stylua.toml with:
line_endings = "Windows"
...and got the following error: config file not in correct format: missing field
indent_type at line 1 column 1
type PromptSettings = {
object: string,
action: string,
holdDuration: number,
keyboardKey: KeyCode,
gamepadKey: KeyCode,
distance: number,
lineOfSight: boolean,
offset: Vector2,
}
... should be kept the same, but it is reformatted as ...
type PromptSettings = { object: string, action: string, holdDuration: number, keyboardKey: KeyCode, gamepadKey: KeyCode, distance: number, lineOfSight: boolean, offset: Vector2, }
StyLua uses multi-line if statement formatting even when the expression will only be put on a single line:
if
not (one and two and three and four and five and six and seven and eight and nine and ten and eleven and twelve and thirteen and fourteen and fifteen and sixteen)
then
end
In this case, I might expect it to remain on one line or be broken up (with a higher indentation).
repeat
until false
will format into:
repeat
until
false
Before running StyLua:
local foo_result = foo( --a comment
"oof"
)
local expr_result = 1 + 2 + 3 + 4 + 5 --a comment
+ 6 + 6 + 8
After running StyLua:
local foo_result = foo( --a comment"oof")
local expr_result = 1 + 2 + 3 + 4 + 5 --a comment + 6 + 6 + 8
local myObj = {
myProp = function(reallyLongVariableNameEatsUpSpace)
if
reallyLongVariableNameEatsUpSpace == "foo"
or reallyLongVariableNameEatsUpSpace == "bar"
or reallyLongVariableNameEatsUpSpace == "baz"
then
return "hello"
end
end,
}
local myObj = {
myProp = function(reallyLongVariableNameEatsUpSpace)
if
reallyLongVariableNameEatsUpSpace == "foo"
or reallyLongVariableNameEatsUpSpace == "bar"
or reallyLongVariableNameEatsUpSpace == "baz"
then
return "hello"
end
end,
}
The then
statement should be aligned with the corresponding if
... end
blocks
- then
+ then
PS: This project is amazing and solving so many issues - hope my edge case detection / bug reporting is helpful, but very grateful to have 95%+ of our formatting auto solved.
Function blocks within expanded expressions seem to base their indentation on that of the line on which the expression begins.
local expr = first_______________________________________________________________________________________________________
and function()
foo()
end
Both my text editor (VS Code) and I expect the indentation to be based on the line where the function block begins:
local expr = first_______________________________________________________________________________________________________
and function()
foo()
end
I say VS Code expects this because that's the indentation level it gives me when I enter onto a new line, starting from the end of function()
Continued from #48 (comment)
return { -- comment
hello = "world",
foo = "bar",
}
... is formatted as ...
return {
-- comment hello = "world",
foo = "bar",
}
When adding something such as *.spec.lua
to .styluaignore then running stylua --check .
a diff is produced for *.spec.lua
files.
Possibly related to #20.
Currently, repeat ... until ...
loops are left aligned to the entire document, which should not happen & should align with the repeat
line.
Test code:
local function Foo(bar)
local count = 0
repeat
count += 1
until count == 10
end
Foo('thing')
Format this and the following is generated:
local function Foo(bar)
local count = 0
repeat
count += 1
until count == 10
end
Foo("thing")
With the recent update, a function call containing comments now becomes expanded, when we do not want it to be
For example:
local App = Roact.createElement("Frame", {
Size = UDim2.fromScale(0.5, 0), -- comment
foo = bar,
}, {
foo = bar
})
should remain the same, however it is incorrectly formatted as
local App = Roact.createElement(
"Frame",
{
Size = UDim2.fromScale(0.5, 0), -- comment
foo = bar,
},
{
foo = bar,
}
)
EDIT: This seems to actually be a regression from v0.2.1
-> v0.3.0
On current main (hash d870087), in a file where I have
type Array<T> = { [number]: T }
I get this error:
error: could not format file definition.lua: error parsing: error occurred while creating ast: unexpected token `type`. (starting from line 30, character 1 and ending on line 31, character 5)
additional information: leftover token
It would be nice if it would skip that AST parent node and proceed, rather than erroring.
In code such as the following, it removes the required semicolon, which breaks the code.
function PlayerPromise.PromiseUserIdFromName(Username: string)
return Promise.Defer(function(Resolve, Reject)
local Success, Value = pcall(Players.GetUserIdFromNameAsync, Players, Username);
(Success and Resolve or Reject)(Value)
end)
end
Below is an example where I'm not entirely sure if it should be wrapped or not:
error(
"Owner `" .. tostring(owner) .. "` already has a pawn. Try deleting their currently owned pawn."
)
This needs to be thought about and then determined on what to do
local x = "foo \"bar\" baz"
... is formatted to ...
local x = "foo \\"bar\\" baz"
and this keeps happening whenever StyLua is run
Input:
if code == 9 -- \t
or code == 32 -- <space>
then
print(code)
end
Output:
if code == 9 -- \t or code == 32 -- <space> then
print(code)
end
Expected (combine code and comments serially):
if code == 9 or code == 32 then -- \t -- <space>
print(code)
end
Expected (do nothing if removing new line will place executing code inside a comment):
if code == 9 -- \t
or code == 32 -- <space>
then
print(code)
end
(same as input)
or do nothing if removing a newline is going to
It would be really useful to have a file similar to a .prettierignore
(something like .styluaignore
possibly) that allows coordination of which files to format without having to pass options to command line every time. This would allow much easier adoption in git projects that are already familiar with this style of collaboration coordination.
Code can get commented out and ruin the file.
Original:
-- Stop Movement
if
-- Moved for at least 0.1 seconds
((tick() - Player.PlayerDataLocal.IsRunningTimeStamp.Value) > 0.1) and -- Speed is less than threshold
(Utility.Vec3XZLengthSquared(Player.Character.PrimaryPart.Velocity) <= RunThresholdSpeedSqr)
then --0.01
Player.PlayerDataLocal.IsRunning.Value = false
end
Formatted:
-- Stop Movement
if
-- Moved for at least 0.1 seconds
((tick() - Player.PlayerDataLocal.IsRunningTimeStamp.Value) > 0.1)
and -- Speed is less than threshold(Utility.Vec3XZLengthSquared(Player.Character.PrimaryPart.Velocity) <= RunThresholdSpeedSqr)
then --0.01
Player.PlayerDataLocal.IsRunning.Value = false
end
function foo()
function bar()
function baz()
--[[
comment
]]
local x = 1
end
end
end
... is formatted as ...
function foo()
function bar()
function baz()
--[[
comment
]]
local x = 1
end
end
end
Currently, the formatted chooses to split lines by determining the number of bytes between the contained span braces (for TableConstructor and FunctionArgs).
This isn't great, as it doesn't take into account the current column width (e.g. if we are a few indents deep)
Column width should be used as the deciding factor about when to introduce line breaks (need to find a way to calculate this as the formatters currently "don't know about their surroundings")
I'm closing this and reopening with a better title. This issue was unintentionally posted before being completed.
The current behavior of formatting a large list that spans multiple lines will put the top and bottom values of a table on the complete left side of the document.
Test code:
local Suffixes = {"k", "M", "B", "T", "qd", "Qn", "sx", "Sp", "O", "N", "de", "Ud", "DD", "tdD", "qdD", "QnD",
"sxD", "SpD", "OcD", "NvD", "Vgn", "UVg", "DVg", "TVg", "qtV", "QnV", "SeV", "SPG", "OVG",
"NVG", "TGN", "UTG", "DTG", "tsTG", "qtTG", "QnTG", "ssTG", "SpTG", "OcTG", "NoTG", "QdDR",
"uQDR", "dQDR", "tQDR", "qdQDR", "QnQDR", "sxQDR", "SpQDR", "OQDDr", "NQDDr", "qQGNT", "uQGNT",
"dQGNT", "tQGNT", "qdQGNT", "QnQGNT", "sxQGNT", "SpQGNT", "OQQGNT", "NQQGNT", "SXGNTL"}
local Object = {ClassName = "Object"}
Object.__tostring = function(self): string
return self.ClassName
end
... is output as ...
local Object = { ClassName = "Object" }
Object.__tostring = function(self)
: string return self.ClassName
end
This input gets wrapped eagerly.
last = last or #array -- If last isn't provided, go to the end
Becoming this, due to the comment.
last = last
or #array -- If last isn't provided, go to the end
However, this remains unchanged.
last = last or #array
Quote style (single quote vs double quote) for strings is a hot topic, including what to do with escapes, consistency etc.
I was wondering whether we should introduce configuration for this specific point.
There would have to be two parts to this:
So, there are two questions to answer here: 1) should we add this configuration option? and 2) what should be our default? [currently our default is double quote - which will remain so - and forcing regardless of escapes - which I'm not sure which is better]
This would be similar to prettier --check
(prettier also uses prettier --write
when formatting the files to distinguish) and would allow quick checks that the project is already compliant with the formatting created by StyLua. This can help with CI checks to ensure everyone is using StyLua in their contributions easily.
There are situations where codebases have strings concatenated together so that they can push them onto multiple lines, to improve the readability.
For example:
local longString = foo(
"We are wrapping this %s "
.. "onto multiple lines "
.. "for ease of editing and %d readability",
myStringVar,
myNumberVar
)
We should keep them expanded if that is the case (with the similar logic as to what we do with already expanded tables)
function foo()
local x = 1
end
... should be formatted as ...
function foo()
local x = 1
end
For consistency across a codebase, we should format number literals if they begin with a decimal.
i.e. .5
would be formatted to 0.5
Input:
local str = '\\"""'
Output:
local str = "\\"\"\""
Expected:
local str = "\\\"\"\""
Preserve the escape on the \
character and then escape the first double quote since the string has been converted to double quotes. It would be nice if strings were formatted to avoidEscape
s, but for simplicity - forcing into double quotes seems fine too.
do
blocks get moved down from their original starting line, which should not occur if it causes no issues.
What I wrote:
local thing = {} do
thing.__index = thing
function thing.new()
return setmetatable({ key = "hi" }, thing)
end
function thing:method()
print(self.key)
end
end
What's formatted:
local thing = {}
do
thing.__index = thing
function thing.new()
return setmetatable({ key = "hi" }, thing)
end
function thing:method()
print(self.key)
end
end
The do
should not move below the line.
Using the example in the readme for avoiding matching .spec.lua
files or modified ones like stylua -g **/*.lua -g !**/*.spec.lua .
just returns error: no files provided
. Ideally this should match against regular .lua
files that are discoverable in the path given.
This seems to be related to the glob option matching multiple patterns following it and the path to run on being treated as a glob. stylua -g **/*.lua .
has this problem, but stylua -g **/*.lua --check .
does not.
Any file I have that includes any mention of the type
keyword cannot be formatted.
Small example:
type Array<T> = { [number]: T }
type Set<T> = { [T]: boolean }
I would be fine if this part of the AST was transferred without any attempts at formatting :)
Stylua should remove excess parentheses where it is safe to do so.
local x
something((x))
should become:
local x
something(x)
However, it's not always safe to remove parentheses. Inside of binary operator expressions, parentheses can denote precedence, so these parentheses should not be removed:
local x = (1 + 2) * 3
Additionally, parentheses can cull extra values returned from a multiple return:
function x()
return 1, 2
end
print(x()) --> 1, 2
print((x())) --> 1
In this example above, removing the parentheses in the expression (x())
is unsafe, because it changes how the code runs.
In cases like this, Stylua should still compress down to one set: print(((x())))
should become print((x()))
.
Comments are treated as trivia and are not preserved in the format
A Format Selection with Stylua
command would make it easier to format small parts of a file. This is useful when you are working on a large file, and only want your new changes to be affected by the formatting.
Comments should ideally be wrapped at a certain line length to maintain readability. Roblox's style guide suggests 80, which is what I use, but I've also seen people use 120 or any random number.
This should ideally apply only to comments that on on lines by themselves (blah() --todo foobar
shouldn't be formatted) and wrap at word boundaries (which can be accomplished with a basic regex pattern). To avoid styling the comment or ruining LDoc, it should maintain any trivia at the beginning of the comment (e.g. preserve ---
and --
).
Hey there! I know you said this is very opinionated but I was wondering which configuration options you are going to/willing to add in the future. I'm very interested in using this as a replacement for LuaFormatter which does allow the following https://github.com/Koihik/LuaFormatter#default-configuration
When adding a directory to ignore then formatting a file in that directory it will apply the formatting to it. I would guess due to not checking the directory opened in VS Code to check for a .styluaignore file.
function foo()
local x = 1
local y = 1
-- comment
end
... is formatted as ...
function foo()
local x = 1
local y = 1
-- comment
end
(Note: A single line comment at the start of a block formats fine)
Currently, if you have a comment at the end of an entry in a table with a comma:
local foo = {
foo = bar, -- this is a comment
bar = baz
}
... it isn't preserved.
In addition, if you have do not have a trailing comma, and have a comment ...
local foo = {
foo = bar,
bar = baz -- comment
}
... StyLua incorrectly places the comma ...
local foo = {
foo = bar,
bar = baz -- comment,
}
We have an unusual edge case which returns the error:
Could not format file: thread 'main' has overflowed its stack
The following block of code fails when including the last expression in the if statement
if
code == 65
or code == 66
or code == 67
or code == 68
or code == 69
or code == 70
or code == 71
or code == 72
or code == 73
or code == 74
or code == 75
or code == 76
or code == 77
or code == 78
or code == 79
or code == 80
or code == 81
or code == 82
or code == 83
or code == 84
or code == 85
or code == 86
or code == 87
or code == 88
or code == 89
or code == 90
or code == 95
or code == 97
or code == 98
or code == 99
or code == 100
or code == 101
or code == 102
or code == 103
or code == 104
or code == 105
or code == 106
or code == 107
or code == 108
or code == 109
or code == 110
or code == 111
or code == 112
or code == 113
or code == 114
or code == 115
or code == 116
then
print("hi")
end
It's definitely a strange edge case, and there are ways to rewrite the code to avoid this, but we have some constraints that prevent excessive rewrites.
Terminal output isn't cleared when using a newline diff at the end of a file - causing any errors shown at the end to be the incorrect colour.
Note: from what I can tell, the colour does seem to clear once the program has exited
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.