Skip to main content
EmmyLua Analyzer includes a built-in code formatter powered by EmmyLuaCodeStyle, enabling consistent code formatting across your team.

Built-in Formatter

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:
.editorconfig
[*.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:
indent_style
space | tab
Use spaces or tabs for indentation
indent_size
number
default:4
Number of spaces per indentation level
max_line_length
number
default:120
Maximum line length before wrapping
quote_style
single | double
Preferred quote style for strings
end_of_line
lf | crlf
Line ending style (LF for Unix/Mac, CRLF for Windows)
insert_final_newline
boolean
default:true
Add newline at end of file
trim_trailing_whitespace
boolean
default:true
Remove trailing whitespace

Style Rules and Diagnostics

Enable code style checking to enforce conventions:
.emmyrc.json
{
  "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:
.emmyrc.json
{
  "diagnostics": {
    "severity": {
      "code-style-check": "warning",
      "naming-convention": "warning"
    },
    "globals": [
      "MAX_RETRY_COUNT",
      "DEFAULT_TIMEOUT"
    ]
  }
}

Project-Wide Formatting

Format on Save

Configure your editor to format on save:
{
  "editor.formatOnSave": true,
  "[lua]": {
    "editor.defaultFormatter": "EmmyLua.emmylua"
  }
}

Pre-commit Hooks

Add formatting to Git pre-commit hooks:
.git/hooks/pre-commit
#!/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

Advanced Formatting Patterns

Table Formatting

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",
}

Function Call Formatting

-- 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)

Comment Formatting

-- 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

.emmyrc.json
{
  "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

.emmyrc.json
{
  "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 .

Migration from Other Formatters

If migrating from other tools:
1

Backup your code

Create a Git commit before reformatting
2

Configure EmmyLuaCodeStyle

Match your previous style settings as closely as possible
3

Format incrementally

Format one module/directory at a time to review changes
4

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

Formatting Not Working

If formatting doesn’t apply:
  1. Check that the formatter is enabled:
    .emmyrc.json
    {
      "reformat": {
        "externalTool": null
      }
    }
    
  2. Verify editor settings for format on save
  3. Check for syntax errors (formatter won’t run on invalid code)

Inconsistent Formatting

If formatting is inconsistent:
  1. Ensure .editorconfig is at project root
  2. Check for conflicting configuration files
  3. 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

Build docs developers (and LLMs) love