This tutorial covers the practical workflow for using the EmmyLua formatter from the command line, configuration files, and library APIs.
Build the formatter binary from this workspace:
cargo build --release -p emmylua_formatterThe formatter executable is luafmt.
Create .luafmt.toml in the project root:
[layout]
max_line_width = 100
table_expand = "Auto"
call_args_expand = "Auto"
func_params_expand = "Auto"
[output]
quote_style = "Preserve"
trailing_table_separator = "Multiline"
single_arg_call_parens = "Preserve"
[comments]
align_in_statements = false
align_in_table_fields = true
align_in_call_args = true
align_in_params = true
[align]
continuous_assign_statement = false
table_field = trueThe formatter discovers the nearest .luafmt.toml or luafmt.toml for each file.
If you want vertically expanded tables to carry trailing separators by default without changing call arguments or parameter lists, set output.trailing_table_separator = "Multiline".
If you want to normalize short-string quoting, set output.quote_style = "Double" or "Single". Long strings are preserved.
Format a directory in place:
luafmt src --writeCheck whether files would change:
luafmt . --checkList only changed paths:
luafmt . --list-differentRead from stdin:
cat script.lua | luafmt --stdinlocal point = { x = 1, y = 2 }some_function(
first_arg, second_arg, third_arg,
fourth_arg
)if alpha_beta_gamma + delta_theta
+ epsilon + zeta then
work()
endfor key, value in first_long_expr,
second_long_expr, third_long_expr,
fourth_long_expr, fifth_long_expr do
print(key, value)
endbuilder
:set_name(name)
:set_age(age)
:build()The formatter is conservative by default:
- statement comment alignment is off
- table, call-arg, and param comment alignment are input-driven
- standalone comments break alignment groups
This is intentional. It avoids manufacturing wide alignment blocks in files that were not written that way originally.
use std::path::Path;
use emmylua_formatter::{check_text_for_path, format_text_for_path};
let path = Path::new("scripts/main.lua");
let formatted = format_text_for_path("local x=1\n", Some(path), None)?;
let checked = check_text_for_path("local x=1\n", Some(path), None)?;
assert!(formatted.output.changed);
assert!(checked.changed);- Commit a shared
.luafmt.toml. - Use
luafmt --checkin CI. - Keep alignment-related options conservative unless the codebase already relies on aligned comments or fields.
- Prefer
Autoexpansion modes unless the project has a strong one-style policy.