Skip to content

mocksoul/dprintx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dprintx

A wrapper around dprint that adds multi-config support and several missing features.

Features

How it works

Config file: ~/.config/dprint/dprintx.jsonc

{
  "dprint": "~/.cargo/bin/dprint",

  "diff_pager": "delta -s",
  "lsp_rewrite_uris": true,

  "profiles": {
    "maintainer": "~/.config/dprint/dprint-maintainer.jsonc",
    "default": "~/.config/dprint/dprint-default.jsonc",
    "ignore": null, // null = skip file entirely
  },

  "match": {
    "**/noc/cmdb/**": "maintainer",
    "**/noc/invapi/**": "maintainer",
    "**": "default",
  },

  "match_content": {
    "^// Code generated .+ DO NOT EDIT": "ignore",
  },
}

Rules in match are evaluated top-to-bottom, first match wins. Files not matching any rule are skipped. Use "**": "profile" as a catch-all. Profiles set to null cause the file to be skipped (passed through unchanged).

All paths in the config (dprint, profile paths) support ~ expansion and relative paths. Relative paths are resolved against the directory containing dprintx.jsonc:

{
  "dprint": "bin/dprint", // → ~/.config/dprint/bin/dprint
  "profiles": {
    "main": "profiles/main.jsonc", // → ~/.config/dprint/profiles/main.jsonc
  },
}

Content-based matching

match_content lets you override the path-matched profile based on file content. This is useful for skipping generated files that live alongside hand-written code.

{
  "match_content": {
    "^// Code generated .+ DO NOT EDIT": "ignore",
    "^# Code generated .+ DO NOT EDIT": "ignore",
  },
}

How it works:

  1. File is first matched by path (match rules) — if no path match, the file is skipped entirely
  2. If match_content is configured, the file is scanned in line-aligned blocks (~8KB each)
  3. Each regex pattern is tested against each block, first match wins (stops scanning early)
  4. The matched profile overrides the path-based result

Patterns are regular expressions (Rust regex syntax, multi-line mode: ^ matches start of any line).

diff_pager

When diff_pager is set, dprint check produces unified diff output instead of dprint's default format:

  • stdout is TTY → pipes through the pager (e.g. delta -s)
  • stdout is pipe/redirect → raw unified diff
dprintx check              # pretty diff via delta
dprintx check > fix.patch  # unified diff to file

Without diff_pager, dprint check behaves exactly like the original dprint.

Local config overrides

Projects can define local formatting rules that override the matched profile.

How it works:

  1. For each file being formatted, dprintx walks up the directory tree looking for dprint.json or dprint.jsonc (stops at the first one found)
  2. If found, it reads the local config and injects the matched profile path into extends
  3. A temporary merged config is written and passed to dprint instead of the profile config
  4. The temp file is auto-deleted when the command finishes (RAII guard)

Since dprint applies extends first and then overlays local settings on top, the local config takes precedence.

Example:

// ~/projects/my-app/dprint.json — only the overrides you care about
{
  "yaml": {
    "commentSpacing": "ignore",
  },
}

When formatting files under ~/projects/my-app/, dprintx generates a temporary config equivalent to:

{
  "extends": "/home/user/.config/dprint/dprint-default.jsonc",
  "yaml": {
    "commentSpacing": "ignore",
  },
}

extends handling:

Local config extends Result
absent set to profile path
"https://example.com/base" ["/profile/path", "https://example.com/base"]
["a.json", "b.json"] ["/profile/path", "a.json", "b.json"]

The profile path is always prepended so that local settings win.

Temp file location: $XDG_RUNTIME_DIR/dprintx/ (per-user, mode 700). Falls back to $TMPDIR/dprintx/ if XDG_RUNTIME_DIR is unavailable. Files are named merged-{pid}-{seq}.json and cleaned up automatically.

If no local config is found, the profile config is used directly — no temp file is created.

Directory arguments

dprint doesn't support directories as arguments (dprint check src/ gives "Is a directory" error). dprintx handles directory arguments by using dprint's own file discovery (output-file-paths) filtered to the specified directories:

dprintx fmt src/                    # format all matched files under src/
dprintx check pkg/internal/drafts   # check all matched files under the directory
dprintx fmt a.go src/ b.rs          # mix of files and directories works too

Files are passed through as-is. Directories use the same pipeline as dprintx fmt/dprintx check without arguments — dprint discovers files via its own includes/excludes, then dprintx filters by profile match rules. This naturally skips binary files, build artifacts, and anything dprint wouldn't process on its own.

LSP URI rewriting (opt-in)

Disabled by default for compatibility. Enable explicitly with "lsp_rewrite_uris": true.

dprint matches files by extension, so extensionless files (e.g. shell scripts named myscript, Lua scripts without .lua) are silently skipped during LSP formatting.

When lsp_rewrite_uris is enabled, the proxy tracks languageId from textDocument/didOpen and rewrites URIs forwarded to the dprint backend by appending the correct extension (e.g. file:///path/myscriptfile:///path/myscript.sh for languageId=sh). If the file already has the correct extension, no rewrite happens.

{
  "lsp_rewrite_uris": true,
}

Default: false (transparent passthrough).

Supported languages:

languageId Extension
go .go
lua .lua
json .json
jsonc .jsonc
yaml .yaml
markdown .md
python .py
rust .rs
typescript .ts
typescriptreact .tsx
javascript .js
javascriptreact .jsx
sh / bash / zsh .sh
toml .toml
css .css
html .html
sql .sql
dockerfile .Dockerfile
graphql .graphql

CLI

# stdin — single file, filename is used for config matching (input is read from stdin)
dprintx fmt --stdin path/to/file.yaml < input.yaml

# fmt/check — groups files by profile, calls dprint per group
dprintx fmt
dprintx check
dprintx fmt file1.go file2.yaml   # explicit file list
dprintx check src/                # directory → recursively expanded

# list all files that would be formatted (merged from all profiles)
dprintx output-file-paths

# show which config is used
dprintx config              # all profiles and rules
dprintx config path/to/file # resolved config for a file

# LSP proxy — spawns dprint lsp per profile, routes by file URI
dprintx lsp

dprintx check exits with code 1 if any files need formatting.

Use --config <PATH> to override the config location (default: ~/.config/dprint/dprintx.jsonc):

dprintx --config /path/to/custom.jsonc fmt

All unknown commands and flags are passed through to the real dprint (--help, -V, license, completions, etc.).

Install

cargo install --git https://github.com/mocksoul/dprintx

Transparent dprint replacement

Symlink dprintx as dprint earlier in your PATH — it becomes a fully transparent drop-in replacement. All unknown commands and flags are forwarded to the real dprint binary (configured via "dprint" in dprintx.jsonc):

ln -sf ~/.cargo/bin/dprintx ~/.local/bin/dprint

Now dprint fmt, dprint check, dprint lsp etc. all go through dprintx automatically. No changes needed in editor configs, CI scripts, or muscle memory.

About

Multi-profile wrapper for dprint — run multiple configs with file matching rules

Resources

License

Stars

Watchers

Forks

Contributors