Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

36 Commits

Repository files navigation

Lua Style Guide

This style guide contains a list of guidelines that we try to follow for our projects. It does not attempt to make arguments for the styles; its goal is to provide consistency across projects.

Feel free to fork this style guide and change to your own liking, and file issues / pull requests if you have questions, comments, or if you find any mistakes or typos.

  1. Types
  2. Tables
  3. Strings
  4. Functions
  5. Properties
  6. Variables
  7. Conditional Expressions & Equality
  8. Blocks
  9. Whitespace
  10. Commas
  11. Semicolons
  12. Type Casting & Coercion
  13. Naming Conventions
  14. Modules
  15. File Structure
  16. Testing
  17. Contributors
  18. License

Types

  • Primitives: When you access a primitive type you work directly on its value

    • string
    • number
    • boolean
    • nil
    localfoo=1localbar=foobar=9print(foo, bar) -- => 1	9
  • Complex: When you access a complex type you work on a reference to its value

    • table
    • function
    • userdata
    localfoo= { 1, 2 }
    localbar=foobar[0] =9foo[1] =3print(foo[0], bar[0]) -- => 9 9print(foo[1], bar[1]) -- => 3 3print(foo[2], bar[2]) -- => 2 2

    [⬆]

Tables

  • Use the constructor syntax for table property creation where possible.

    -- badlocalplayer= {}
    player.name='Jack'player.class='Rogue'-- goodlocalplayer= {
    name='Jack',
    class='Rogue'
    }
  • Define functions externally to table definition.

    -- badlocalplayer= {
    attack=function()
    -- ...stuff...end
    }
    -- goodlocalfunctionattack()
    endlocalplayer= {
    attack=attack
    }
  • Consider nil properties when selecting lengths. A good idea is to store an n property on lists that contain the length (as noted in Storing Nils in Tables)

    -- nils don't countlocallist= {}
    list[0] =nillist[1] ='item'print(#list) -- 0print(select('#', list)) -- 1
  • When tables have functions, use self when referring to itself.

    -- badlocalme= {
    fullname=function(this)
    returnthis.first_name+'' +this.last_nameend
    }
    -- goodlocalme= {
    fullname=function(self)
    returnself.first_name+'' +self.last_nameend
    }

    [⬆]

Strings

  • Use single quotes '' for strings.

    -- badlocalname="Bob Parr"-- goodlocalname='Bob Parr'-- badlocalfull_name="Bob " ..self.last_name-- goodlocalfull_name='Bob ' ..self.last_name
  • Strings longer than 80 characters should be written across multiple lines using concatenation. This allows you to indent nicely.

    -- badlocalerror_message='This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'-- badlocalerror_message='This is a super long error that \was thrown because of Batman. \When you stop to think about \how Batman had anything to do \with this, you would get nowhere \fast.'-- badlocalerror_message=[[This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.]]-- goodlocalerror_message='This is a super long error that ' ..'was thrown because of Batman. ' ..'When you stop to think about ' ..'how Batman had anything to do ' ..'with this, you would get nowhere ' ..'fast.'

    [⬆]

Functions

  • Prefer lots of small functions to large, complex functions. Smalls Functions Are Good For The Universe.

  • Prefer function syntax over variable syntax at the top level.

    -- AVOIDlocalnope=function(name, options)
    -- ...stuff...end-- BETTERlocalfunctionyup(name, options)
    -- ...stuff...end
  • Never name a parameter arg, this will take precendence over the arg object that is given to every function scope in older versions of Lua.

    -- badlocalfunctionnope(name, options, arg)
    -- ...stuff...end-- goodlocalfunctionyup(name, options, ...)
    -- ...stuff...end
  • Perform validation early and return as early as possible.

    -- badlocalis_good_name=function(name, options, arg)
    localis_good=#name>3is_good=is_goodand#name<30-- ...stuff...returnis_badend-- goodlocalis_good_name=function(name, options, args)
    if#name<3or#name>30thenreturnfalseend-- ...stuff...returntrueend

[⬆]

Properties

  • Use dot notation when accessing known properties.

    localluke= {
    jedi=true,
    age=28
    }
    -- badlocalis_jedi=luke['jedi']
    -- goodlocalis_jedi=luke.jedi
  • Use subscript notation [] when accessing properties with a variable or if using a table as a list.

    localluke= {
    jedi=true,
    age=28
    }
    localfunctionget_prop(prop)
    returnluke[prop]
    endlocalis_jedi=get_prop('jedi')

    [⬆]

Variables

  • Always use local to declare variables. Not doing so will result in global variables to avoid polluting the global namespace.

    -- badsuperPower=SuperPower()
    -- goodlocalsuperPower=SuperPower()
  • Assign variables at the top of their scope where possible. This makes it easier to check for existing variables.

    -- badlocalbad=function()
    test()
    print('doing stuff..')
    //..otherstuff..localname=get_name()
    ifname=='test' thenreturnfalseendreturnnameend-- goodlocalfunctiongood()
    localname=get_name()
    test()
    print('doing stuff..')
    //..otherstuff..ifname=='test' thenreturnfalseendreturnnameend

    [⬆]

Conditional Expressions & Equality

  • False and nil are falsy in conditional expressions. All else is true.

    localstr=''ifstrthen-- trueend
  • Use shortcuts when you can, unless you need to know the difference between false and nil.

    -- badifname~=nilthen-- ...stuff...end-- goodifnamethen-- ...stuff...end
  • Prefer true statements over false statements where it makes sense. Prioritize truthy conditions when writing multiple conditions.

    --badifnotthingthen-- ...stuff...else-- ...stuff...end--goodifthingthen-- ...stuff...else-- ...stuff...end
  • Prefer defaults to else statements where it makes sense. This results in less complex and safer code at the expense of variable reassignment, so situations may differ.

    --badlocalfunctionfull_name(first, last)
    localnameiffirstandlastthenname=first..'' ..lastelsename='John Smith'endreturnnameend--goodlocalfunctionfull_name(first, last)
    localname='John Smith'iffirstandlastthenname=first..'' ..lastendreturnnameend
  • Short ternaries are okay.

    localfunctiondefault_name(name)
    -- return the default 'Waldo' if name is nilreturnnameor'Waldo'endlocalfunctionbrew_coffee(machine)
    returnmachineandmachine.is_loadedand'coffee brewing' or'fill your water'end

    [⬆]

Blocks

  • Single line blocks are okay for small statements. Try to keep lines to 80 characters. Indent lines if they overflow past the limit.

    -- goodiftestthenreturnfalseend-- goodiftestthenreturnfalseend-- badiftest<1anddo_complicated_function(test) ==falseorseven==8andnine==10thendo_other_complicated_function()end-- goodiftest<1anddo_complicated_function(test) ==falseorseven==8andnine==10thendo_other_complicated_function()
    returnfalseend

    [⬆]

Whitespace

  • Use two spaces for indentation.

    -- badfunction()
    ∙∙∙∙localnameend-- badfunction()
    ∙localnameend-- goodfunction()
    ∙∙localnameend
  • Place 1 space before opening and closing braces. Place no spaces around parens.

    -- badlocaltest= {one=1}
    -- goodlocaltest= { one=1 }
    -- baddog.set('attr',{
    age='1 year',
    breed='Bernese Mountain Dog'
    })
    -- gooddog.set('attr', {
    age='1 year',
    breed='Bernese Mountain Dog'
    })
  • Place an empty newline at the end of the file.

    -- bad
    (function(global)
    -- ...stuff...end)(self)
    -- good
    (function(global)
    -- ...stuff...end)(self)
    
  • Surround operators with spaces.

    -- badlocalthing=1thing=thing-1thing=thing*1thing='string'..'s'-- goodlocalthing=1thing=thing-1thing=thing*1thing='string' ..'s'
  • Use one space after commas.

    --badlocalthing= {1,2,3}
    thing= {1 , 2 , 3}
    thing= {1 ,2 ,3}
    --goodlocalthing= {1, 2, 3}
  • Add a line break after multiline blocks.

    --badifthingthen-- ...stuff...endfunctionderp()
    -- ...stuff...endlocalwat=7--goodifthingthen-- ...stuff...endfunctionderp()
    -- ...stuff...endlocalwat=7
  • Delete unnecessary whitespace at the end of lines.

    [⬆]

Commas

  • Leading commas aren't okay. An ending comma on the last item is okay but discouraged.

    -- badlocalthing= {
    once=1
    , upon=2
    , aTime=3
    }
    -- goodlocalthing= {
    once=1,
    upon=2,
    aTime=3
    }
    -- okaylocalthing= {
    once=1,
    upon=2,
    aTime=3,
    }

    [⬆]

Semicolons

  • Nope. Separate statements onto multiple lines.

    -- badlocalwhatever='sure';
    a=1; b=2-- goodlocalwhatever='sure'a=1b=2

    [⬆]

Type Casting & Coercion

  • Perform type coercion at the beginning of the statement. Use the built-in functions. (tostring, tonumber, etc.)

  • Use tostring for strings if you need to cast without string concatenation.

    -- badlocaltotal_score=review_score..''-- goodlocaltotal_score=tostring(review_score)
  • Use tonumber for Numbers.

    localinput_value='4'-- badlocalval=input_value*1-- goodlocalval=tonumber(input_value)

    [⬆]

Naming Conventions

  • Avoid single letter names. Be descriptive with your naming. You can get away with single-letter names when they are variables in loops.

    -- badlocalfunctionq()
    -- ...stuff...end-- goodlocalfunctionquery()
    -- ..stuff..end
  • Use underscores for ignored variables in loops.

    --goodfor_, nameinpairs(names) do-- ...stuff...end
  • Use snake_case when naming objects, functions, and instances. Tend towards verbosity if unsure about naming.

    -- badlocalOBJEcttsssss= {}
    localthisIsMyObject= {}
    localthis-is-my-object= {}
    localc=function()
    -- ...stuff...end-- goodlocalthis_is_my_object= {}
    localfunctiondo_that_thing()
    -- ...stuff...end
  • Use PascalCase for factories.

    -- badlocalplayer=require('player')
    -- goodlocalPlayer=require('player')
    localme=Player({ name='Jack' })

    [⬆]

  • Use is or has for boolean-returning functions that are part of tables.

    --badlocalfunctionevil(alignment)
    returnalignment<100end--goodlocalfunctionis_evil(alignment)
    returnalignment<100end

Modules

  • The module should return a table or function.

  • The module should not use the global namespace for anything ever. The module should be a closure.

  • The file should be named like the module.

    -- thing.lualocalthing= { }
    localmeta= {
    __call=function(self, key, vars)
    printkeyend
    }
    returnsetmetatable(thing, meta)
  • Note that modules are loaded as singletons and therefore should usually be factories (a function returning a new instance of a table) unless static (like utility libraries.)

[⬆]

File Structure

  • Files should be named in all lowercase.

  • Lua files should be in a top-level src folder. The main library file should be called modulename.lua.

  • Rockspecs, license, readme, etc should be in the top level.

  • Tests should be in a top-level spec folder.

  • Executables should be in a top-level bin folder.

  • Example:

    ./my_module
    bin/
    script.sh
    spec/
    my_module_spec.lua
    some_file.lua
    src/
    my_module.lua
    some_file.lua
    README.md
    LICENSE.md
    

Testing

  • Use busted and write lots of tests in a /spec folder. Separate tests by module.

  • Use descriptive describe and it blocks so it's obvious to see what precisely is failing.

  • Test interfaces. Don't test private methods. If you need to test something that is private, it probably shouldn't be private in the first place.

  • Example:

    ./my_module
    bin/
    script.sh
    spec/
    my_module_spec.lua
    util/
    formatters_spec.lua
    src/
    my_module.lua
    util/
    formatters.lua
    README.md
    LICENSE.md
    

    [⬆]

Contributors

License

[⬆]

About

Lua style guide

Resources

Stars

9 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors