EmmyLua Analyzer includes a built-in code formatter powered by EmmyLuaCodeStyle , enabling consistent code formatting across your team.
The analyzer includes EmmyLuaCodeStyle, a professional Lua formatter with extensive configuration options.
Basic Usage
Format your code through your editor’s format command (usually Shift+Alt+F in VS Code or :Format in Neovim).
The built-in formatter automatically reads .editorconfig files for consistent formatting across your project.
EditorConfig Integration
Create a .editorconfig file in your project root:
[*.lua]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
Code Style Configuration
Create a .editorconfig file or .stylua.toml for detailed style configuration:
Use spaces or tabs for indentation
Number of spaces per indentation level
Maximum line length before wrapping
Preferred quote style for strings
Line ending style (LF for Unix/Mac, CRLF for Windows)
Add newline at end of file
Remove trailing whitespace
Style Rules and Diagnostics
Enable code style checking to enforce conventions:
{
"diagnostics" : {
"enable" : true ,
"severity" : {
"code-style-check" : "warning"
}
}
}
Code style diagnostics help maintain consistency but don’t replace proper formatting. Use both together for best results.
Team Style Guides
Establishing Team Conventions
Create a team style guide by documenting your conventions:
1. Naming Conventions
-- Classes: PascalCase
--- @class PlayerController
local PlayerController = {}
-- Functions: camelCase or snake_case (pick one)
function PlayerController : movePlayer () end
-- or
function PlayerController : move_player () end
-- Constants: UPPER_SNAKE_CASE
local MAX_HEALTH = 100
local DEFAULT_SPEED = 5
-- Private members: prefix with underscore
PlayerController . _privateData = {}
function PlayerController : _internalMethod () end
2. Annotation Style
Consistent annotation placement:
-- Good: Annotations directly above declarations
--- @class Vector3
--- @field x number
--- @field y number
--- @field z number
local Vector3 = {}
--- @param x number
--- @param y number
--- @param z number
--- @return Vector3
function Vector3 . new ( x , y , z )
return setmetatable ({ x = x , y = y , z = z }, { __index = Vector3 })
end
-- Good: Group related fields
--- @class Player
--- @field name string
--- @field health number
--- @field maxHealth number
--- @field position Vector3
--- @field velocity Vector3
3. Module Structure
Standardize module organization:
-- Module header
--- @class MyModule
--- @field version string
local MyModule = {
version = "1.0.0"
}
-- Private constants
local CONSTANT_A = 1
local CONSTANT_B = 2
-- Private functions
local function helperFunction ()
-- implementation
end
-- Public functions
function MyModule . publicFunction ()
return helperFunction ()
end
return MyModule
Enforcing Conventions
Use diagnostic configuration to enforce naming:
{
"diagnostics" : {
"severity" : {
"code-style-check" : "warning" ,
"naming-convention" : "warning"
},
"globals" : [
"MAX_RETRY_COUNT" ,
"DEFAULT_TIMEOUT"
]
}
}
Configure your editor to format on save:
VS Code settings.json
Neovim
{
"editor.formatOnSave" : true ,
"[lua]" : {
"editor.defaultFormatter" : "EmmyLua.emmylua"
}
}
Pre-commit Hooks
Add formatting to Git pre-commit hooks:
#!/bin/bash
# Get all staged Lua files
LUA_FILES = $( git diff --cached --name-only --diff-filter=ACMR | grep '\.lua$' )
if [ -n " $LUA_FILES " ]; then
# Format files using emmylua_ls
for file in $LUA_FILES ; do
emmylua_ls --format " $file " --output " $file "
git add " $file "
done
fi
Control table formatting style:
-- Inline tables (short)
local point = { x = 0 , y = 0 }
-- Multi-line tables (long or complex)
local config = {
host = "localhost" ,
port = 8080 ,
timeout = 30 ,
retries = 3 ,
headers = {
[ "Content-Type" ] = "application/json" ,
[ "Authorization" ] = "Bearer token"
}
}
-- Array-style tables
local items = {
"item1" ,
"item2" ,
"item3" ,
}
-- Short calls: single line
local result = calculate ( 1 , 2 , 3 )
-- Long calls: break into multiple lines
local user = createUser (
"John Doe" ,
"[email protected] " ,
{ admin = true , active = true }
)
-- Chain calls: one per line
local result = data
: filter ( isValid )
: map ( transform )
: reduce ( sum )
-- Single-line comments for brief explanations
local timeout = 30 -- seconds
--[[
Multi-line comments for detailed explanations
that span multiple lines and need more context.
]]
---@doc Block for documentation
--- that spans multiple lines
--- and describes complex behavior
function complexFunction ()
end
Custom Style Rules
Define custom style rules for your project:
Documentation Requirements
{
"diagnostics" : {
"severity" : {
"missing-global-doc" : "warning" ,
"incomplete-signature-doc" : "warning" ,
"undefined-doc-param" : "error"
}
}
}
This enforces:
All global functions must have documentation
Function signatures must be complete
All documented parameters must exist
Visibility Rules
{
"diagnostics" : {
"severity" : {
"access-invisible" : "error"
}
}
}
Enforces visibility modifiers:
--- @class MyClass
local MyClass = {}
--- @private
function MyClass : _internal ()
end
---@public
function MyClass : public ()
self : _internal () -- OK: same class
end
-- External code
local obj = MyClass . new ()
obj : _internal () -- Error: accessing private member
Workspace-Wide Style Consistency
Style Configuration Files
Share configuration across team:
project/
├── .emmyrc.json
├── .editorconfig
├── .gitignore
└── README.md
CI/CD Integration
Add style checking to CI pipeline:
.github/workflows/lint.yml
name : Lint
on : [ push , pull_request ]
jobs :
style-check :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v3
- name : Install EmmyLua
run : cargo install emmylua_check
- name : Check code style
run : emmylua_check --check-style .
If migrating from other tools:
Backup your code
Create a Git commit before reformatting
Configure EmmyLuaCodeStyle
Match your previous style settings as closely as possible
Format incrementally
Format one module/directory at a time to review changes
Update documentation
Document the new style guide for your team
Style Guide Examples
--- @class Game
local Game = {}
function Game : load ()
self . player = {
x = 400 ,
y = 300 ,
speed = 200
}
end
function Game : update ( dt )
if love . keyboard . isDown ( "right" ) then
self . player . x = self . player . x + self . player . speed * dt
end
end
function Game : draw ()
love . graphics . circle ( "fill" , self . player . x , self . player . y , 20 )
end
return Game
--- @class RequestHandler
local _M = { _VERSION = '1.0.0' }
local cjson = require "cjson"
function _M . handle_request ()
local args = ngx . req . get_uri_args ()
if not args . id then
ngx . status = ngx . HTTP_BAD_REQUEST
ngx . say ( cjson . encode ({ error = "Missing id parameter" }))
return ngx . exit ( ngx . HTTP_BAD_REQUEST )
end
-- Handle request...
end
return _M
Troubleshooting
If formatting doesn’t apply:
Check that the formatter is enabled:
{
"reformat" : {
"externalTool" : null
}
}
Verify editor settings for format on save
Check for syntax errors (formatter won’t run on invalid code)
If formatting is inconsistent:
Ensure .editorconfig is at project root
Check for conflicting configuration files
Verify all team members use the same formatter version
Next Steps
External Formatters Use StyLua or other external formatters
Type Checking Configure advanced type checking rules