EditorConfig and a Predictable Save
Define formatter ownership and EditorConfig conventions for reproducible editor and CLI results.
Sources checked on 2026-09-06. Apply the examples sequentially in a separate format-lab project. Commands run from its root on macOS, Linux, or WSL. Merge the relevant keys into existing configuration files.
Start with what a save changes
When CI rejects a file that looked fine after saving, compare four things: executable, configuration, file scope, and working directory. This lab installs tools directly in each project and commits small configuration files. Consider a shared package once multiple projects actually need to change the same policy together.
| Files | Formatter | Rule checks | Additional checks |
|---|---|---|---|
| JS·TS | Oxfmt | Oxlint | TypeScript |
| YAML | Oxfmt | yamllint | Schema and consuming application |
| Shell | shfmt | ShellCheck | bash -n and execution tests |
| Go | gofmt | go vet and gopls | go test |
| Python | Ruff format | Ruff check | Type checking and tests |
A formatter changes layout. A linter reports suspicious code or policy violations; some diagnostics have automatic fixes. EditorConfig communicates text conventions and does not run these programs.
Create the lab and text conventions
Install VS Code and make its code command available on PATH. If unavailable, use the command palette's PATH installation command or open the folder through the UI.
mkdir format-lab
cd format-lab
git init
mkdir -p .vscode src config scripts go-example python-example
code --install-extension EditorConfig.EditorConfig
code .Create .editorconfig at the root. Python gets four spaces; Go and Makefiles get tabs. Preserve meaningful Markdown trailing spaces. A width preference is not a command to split every long line.
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_size = 4
[*.{md,mdx}]
trim_trailing_whitespace = false
[*.go]
indent_style = tab
indent_size = tab
[{Makefile,*.mk}]
indent_style = tab
indent_size = tabCompatible editors apply matching sections; the standard root = true stops parent discovery. Individual formatters can implement only part of that behavior. EditorConfig rules
Run Oxfmt from the lab root: it reads supported properties as fallbacks, with limitations on nested EditorConfig merging. Ruff explicitly configures Ruff width in pyproject.toml; gofmt uses Go conventions. Oxfmt EditorConfig support
Connect saving in .vscode/settings.json
{
"files.autoSave": "off",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.detectIndentation": false
}These are shared save settings. Add language-specific formatter providers in subsequent lab pages. Disabling indentation detection makes this lab follow settings instead of inferring indentation from existing text.
User, workspace, and language-specific settings have different scopes. A global formatter does not remove another extension's save actions. Open Format Document With… → Configure Default Formatter on the actual file to inspect its provider. VS Code settings precedence
Learning sequence
- Oxfmt and Oxlint: distinguish layout from code diagnostics.
- YAML: separate formatting, lint rules, and schemas.
- Shell: reproduce a ShellCheck problem that formatting cannot fix.
- Go: turn gofmt differences into a failing CI check.
- Python: connect format, lint, and import sorting.
- Tasks and CI: reuse the same checking commands.
Check new-line indentation in a Python file and a Makefile using the status bar and whitespace rendering. In the next part, save, run CLI checks, and save again. Completion means that the second cycle produces no new changes.
Python Environments, Pylance, pytest, and debugpy
Configure the environment, editor, tests, type checks, and debugger to use the same Python project.
Connect Oxfmt and Oxlint Directly
Install Oxfmt and Oxlint, connect JSON configuration and save actions, and reproduce the difference between formatting and diagnostics.