Skip to main content

Editors

dfmt formats the buffer you are editing, not the file on disk, and it formats a dbt model as that model: with its config, its refs and the project's macros. Editors reach it through the language server, dfmt lsp. It stays running for the editor window, so a dbt project is loaded once instead of on every save, and it reads the same dlab.json every dfmt command reads: set the project up once with dfmt setup, and the editor formats exactly as dfmt write does.

VS Code, Cursor, Windsurf​

In the Extensions view, search for Datoria and install the extension called dlab, by Datoria. It is in both the Visual Studio Marketplace and Open VSX. It formats .sql files whatever language mode they are in, so it works beside dbt Power User and Better Jinja, and it registers for their jinja-sql mode too.

The first time it opens a workspace containing a dbt project, it offers to become the default formatter for SQL and dbt models and to format on save. To do the same by hand, put this in .vscode/settings.json:

{
"[sql]": {
"editor.defaultFormatter": "datoria.dlab",
"editor.formatOnSave": true
},
"[jinja-sql]": {
"editor.defaultFormatter": "datoria.dlab",
"editor.formatOnSave": true
}
}

The default formatter setting matters: with two formatters registered for a language and no default, VS Code asks you to choose and formats with neither until you do.

You do not need to install dfmt first. The extension starts the language server from the dfmt you installed when it finds it on your PATH. When there is none, it downloads the release for your platform itself, into ~/Library/Caches/dlab on macOS, ~/.cache/dlab on Linux or %LOCALAPPDATA%\dlab on Windows. A "version" in your project's dlab.json, or the dlab.version setting, pins which release it downloads. The dlab.path setting points it at a specific binary instead.

What you see. Warnings dfmt reports about the file appear in the Problems panel at the line they concern. The status bar shows the last result. When dfmt declines to format a file, the buffer is left exactly as it was and the reason is in the Problems panel and the extension's output channel. A file outside any dbt project or dlab.json has no dialect: run dfmt setup in the project.

The options pane. Run dlab: Open the formatting options pane with an SQL file open. A panel opens beside the editor with one control per formatting option, plus the dialect. Every change is written to the project's dlab.json and the open file is reformatted at once, so you can keep the pane open and try things. Options left at their default are not written, the same rule dfmt setup follows. The pane shows whatever dfmt options --json reports, so it always matches the binary in use. The ruler at the line width follows maxLineWidth as you change it.

Neovim​

Neovim 0.11 and later start the language server with no plugin:

vim.lsp.config("dfmt", {
cmd = { "dfmt", "lsp" },
filetypes = { "sql" },
root_markers = { "dlab.json", "dbt_project.yml", ".git" },
})
vim.lsp.enable("dfmt")

vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.sql",
callback = function(args)
vim.lsp.buf.format({ bufnr = args.buf, name = "dfmt" })
end,
})

Emacs​

With eglot, built into Emacs 29 and later:

(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(sql-mode . ("dfmt" "lsp"))))
(add-hook 'sql-mode-hook #'eglot-ensure)
(add-hook 'sql-mode-hook
(lambda () (add-hook 'before-save-hook #'eglot-format-buffer nil t)))

JetBrains IDEs​

Settings → Tools → File Watchers → add a watcher for SQL files with program dfmt, arguments write $FilePath$, and "Trigger the watcher on external changes" off. This formats the saved file rather than the buffer, which is the only hook those IDEs offer without a plugin.

Any other editor​

Point the editor's language-server client at dfmt lsp, which speaks the Language Server Protocol over stdin and stdout. It answers whole-document formatting requests and reports dfmt's warnings as diagnostics. When dfmt declines to format a file, the answer is no edits, and the reason is a diagnostic.