The official SEBRP Glua styling guidelines as used on most of our repositories and other work.
This is forked from CFC Servers (https://github.com/CFC-Servers) but edited because we want some things slightly different.
Use GLuaLint (GLuaFixer) as your linter. There are plugins available for most major editors.
GLuaFixer: https://github.com/FPtje/GLuaFixer
Use our GLuaFixer config, found here: https://github.com/SEBRP/glua_style_guidelines/blob/main/glualint.json
Good
local x = a * b + c
Bad
local x = a* b+c
Good
myFunc(10, {3, 5})
Bad
myFunc( 10,{ 3,5 })
Good
if cond then
myFunc()
end
Bad
if cond then
myFunc()
end
Good
local val = tab[5] + tab[3]
local val2 = tab[5 * 3]
Bad
local val = tab[ 5 ] + tab[ 3 ]
local val2 = tab[ 5 * 3 ]
Good
-- This is a good comment
local a = 3 -- This is also good
Bad
--This comment doesn't have a space before it
local a = 3-- This comment starts too close to the 3
Good
local config = GM.Config
function GM:Think()
-- do thing
end
Bad
local config = GM.Config
function GM:Think()
-- do thing
end
Good
local config = GM.Config
function GM:Think()
-- do thing
end
Bad
local config = GM.Config
function GM:Think()
-- do thing
end
Good
function test()
local a = 3
print( a )
end
Bad
function test()
local a = 3
print( a )
end
Good
function test()
local a = 3
return a
end
function test2()
return 3
end
Bad
function test()
local a = 3
return a
end
Good
function CFCNotifications.resolveFilter( filter )
if type( filter ) == "Player" then
filter = { filter }
end
if type( filter ) == "table" then
filter = fWrap( filter )
end
filter = filter or player.GetAll
local players = filter()
if type( players ) == "Player" then
players = { players }
end
if not players then players = player.GetAll() end
return players
end
Bad, far too dense and difficult to read at a glance
function CFCNotifications.resolveFilter( filter )
if type( filter ) == "Player" then
filter = { filter }
end
if type( filter ) == "table" then
filter = fWrap( filter )
end
filter = filter or player.GetAll
local players = filter()
if type( players ) == "Player" then
players = { players }
end
if not players then players = player.GetAll() end
return players
end
Good
if a ~= b and not b then end
Bad, Garry's operators aren't recognized by standard Lua highlighting
if a != b && !b then end
Good
--[[
This line does stuff
]]
do( stuff ) -- Stuff being done
Bad, gmod comments aren't recognized by standard Lua highlighting
/*
This line does stuff
*/
do( stuff ) // Stuff being done
Good
local myVariable = 10
Bad
local MyVariable = 10
local my_variable = 20
Good
CATEGORY_NAME = "nothing"
local MAXIMUM_VALUE = 25
Bad
CategoryName = "nothing"
categoryName = "nothing"
category_name = "nothing"
local maximumValue = 25
Good
GlobalVariable = 10
Bad
globalVariable = 20
global_variable = 20
Good
function classTable:SetHealth( amount )
end
Bad
function classTable:setHealth( amount )
end
Good
myTable.myValue = 4
Bad, accessing the table value requires brackets and quotes because of the hyphen
myTable["my-value"] = 4
Good
for _, ply in pairs( player.GetAll() ) do
local _, shouldKill = ply:GetKillData()
if shouldKill then
ply:Kill()
end
end
Bad, k
isn't used
for k, ply in pairs( player.GetAll() ) do
local canKill, shouldKill = ply:GetKillData()
if shouldKill then
ply:Kill()
end
end
- Hook identifiers should be named as such
AddonName_HookPurpose
- The hook event name should not be included in the identifier.
For example, you should not do
ORG_MyAddon_OnDisconnect
or evenORG_MyAddon_CleanupPropsOnDisconnect
. ButORG_MyAddon_CleanupProps
would be appropriate. The "HookPurpose" should state what a function does without restating information provided by the event name. - Hook event names should be named as such
Organization_EventName
Good, quote usage is consistent
myFunc("hello ", "world!")
Bad, different quotes are used interchangeably
myFunc( "hello ", 'world!' )
Good
if x == y then
Bad, these parenthesis are not necessary
if ( x == y ) then
Redundant parenthesis may be used if it makes the order of operations clear. The author should use their best judgement
It's important for the author to remember who their target audience is. Not every reader will be good with maths, and it can be helpful to make equations easy to follow
Acceptable
local amount = ( quantity * modifier ) + baseAmount
Unsavory, but fine
local amount = quantity * modifier + baseAmount
Elements should begin on the next line and the last line should contain only a closing bracket. Elements inside should be indented once
Good
tbl = {
key = x,
otherKey = y
}
Bad, this indentation is inconsistent and can make it difficult for the reader to parse
tbl = { key = x,
otherKey = y}
Good
myFunc("First arg",
secondArg,
{ third, arg }
)
Bad, this indentation is inconsistent and objectively wrong
myFunc( "First arg",
secondArg, { third, arg } )
Good
function test()
if not myThing then return end
-- Do a bunch of real complicated things
end
Bad, adds an extra level of indentation
function test()
if myThing then
-- Do a bunch of real complicated things
end
end
Good
local maxX = 25
function checkX( x )
return x > maxX
end
Bad, the significance of 25
is unknown without a meaningful variable name
function checkX( x )
return x > 25
end
Good, each step of the equation is named and done individually. The math is easy to follow This is not necessary if the equation is good commented
local widthModifier = amount * damageMult
local age = 1 - lifetime / duration
local width = widthModifier * age
Bad, this math is difficult to figure out from a glance
local width = ( amount * 5 ) * ( 1 - lifetime / duration )
Semicolons provide no functional value in Lua. While they can be used to delimit table items, a comma is preferred
Good
local a = 3
print( a )
return a
Bad, this exclusively makes the code harder to read
local a = 3; print( a );
return a;
Good
x = y * math.pi
radians = math.rad( deg )
Bad, the meaning of 3.142
may not be immediately clear. It also suffers a loss in precision compared to math.pi
**
x = y * 3.142
radians = deg * ( 3.142 / 180 )
Good, each check is clear and it's easy to follow the reasoning
if IsValid( ent )
and ent:GetCPPIOwner():GetVehicle():GetColor().a > 0
and ( ent:GetCPPIOwner():IsAdmin() or ent:GetCPPIOwner().canDoThing ) then
-- do thing
end
Bad, the conditions are difficult to follow and require some unpacking
if IsValid( ent ) and ent:GetCPPIOwner():GetVehicle():GetColor().a > 0 and ( ent:GetCPPIOwner():IsAdmin() or ent:GetCPPIOwner().canDoThing ) then
-- do thing
end
Good
if IsValid(ent) and ent:IsPlayer() and ent:IsAdmin() then
local hue = ( CurTime() * 10 ) % 360
local color = HSVToColor(hue, 1, 1))
ent:SetColor(color)
end
Bad, this line is too long and could easily be split into multiple, easy-to-follow lines
if IsValid( ent ) and ent:IsPlayer() and ent:IsAdmin() then ent:SetColor( HSVToColor( ( CurTime() * 10 ) % 360, 1, 1 ) ) ) end
While you can omit a zero in decimals in the range of 0-1, it's an antipattern and a one-off exception when compared to decimals outside of said range
Good
local num = 0.69
Bad, while the 0 is implied, it can make it harder to parse at a glance
local num = .69
Lua numbers can technically be prefixed with as many 0s as you'd like, but in most cases it's completely unnecessary
Good
local factor = 420
Bad, just why?
local factor = 00420
Except in cases where the trailing 0s make it easier to understand the values (e.g. when you have a column of configurable decimals with varying precision), there is no reason to include them and could confuse the reader
Good
local decimal = 0.2
Bad
local decimal = 0.200
Good variable and function names can make comments unecessary. Strive for self commenting code. Save comments for complicated code that may not be clear on its own
Good
for _, ply in pairs( players ) do
print( ply )
if ply:Alive() then ply:Kill() end
end
Bad, the code explains itself without comments
for _, v in pairs( stuff ) do -- loop through players
print( v ) -- print the player
if v:Alive() then v:Kill() end -- kill player if player is alive
end