Skip to content

Latest commit

 

History

History
98 lines (74 loc) · 3.51 KB

File metadata and controls

98 lines (74 loc) · 3.51 KB

scalafmt — scalafmt-direct

Per-workspace scalafmt coordinator. One-shot mode — each call runs scalafmt as a subprocess. The native binary's sub-second cold start already beats the <300ms target a persistent-JVM adapter would aim for (see "Why no persistent-JVM adapter" below).

Install prereq

scalafmt CLI binary on PATH. Options in order of robustness:

# native binary (fastest cold-start; arch-specific release asset)
#   see https://github.com/scalameta/scalafmt/releases/latest
#   macOS arm64:
curl -L -o /tmp/sf.zip https://github.com/scalameta/scalafmt/releases/download/v3.11.0/scalafmt-aarch64-apple-darwin.zip \
  && unzip -o /tmp/sf.zip -d ~/.local/bin \
  && chmod +x ~/.local/bin/scalafmt

# coursier-launched JVM (drops in on any Scala-dev machine)
cs install scalafmt

The native binary binds one scalafmt version per download; bump when your .scalafmt.conf's version = pin changes.

Workspace markers (walk-up order)

  1. .scalafmt.conf
  2. build.sbt
  3. build.sc
  4. build.mill

Invocation

scalafmt-direct call version      '{}'
scalafmt-direct call format-stdin '{"source":"object A{}","filepath":"A.scala"}'
scalafmt-direct call format-files '{"files":["src/main/scala/A.scala","src/main/scala/B.scala"]}'
scalafmt-direct call check-files  '{"files":["src/main/scala/A.scala"]}'

Method surface

method params wraps
version {} scalafmt --version
format-stdin {source, filepath?} scalafmt --stdin [--stdin-filename <p>] (stdout = formatted)
format-files {files: [abs-path...]} scalafmt --non-interactive <files...> (rewrites in place)
check-files {files: [abs-path...]} scalafmt --test --non-interactive <files...> (exit !=0 on diff)

Results are {exit, signal, stdout, stderr} from the subprocess.

Timing

  • Native binary: 0.3-0.8s per call (no JVM boot).
  • Coursier-launched JVM: 3-5s per call (JVM boot + classloader + conf parse).

Verified smokes

  • scalafmt-direct call version {}{exit: 0, stdout: "scalafmt 3.11.0"}.
  • scalafmt-direct call check-files {"files": ["src/main/scala/Hello.scala"]} on a version-matched fixture → {exit: 0, stdout: "All files are formatted with scalafmt :)"}.
  • scalafmt-direct call format-stdin {"source": "object A{def x=1}", "filepath": "A.scala"}{exit: 0, stdout: "object A { def x = 1 }"}.

Invalidation matrix

type files action
soft .scalafmt.conf no-op (scalafmt re-reads the conf on every invocation)
hard .env, .env.local coordinator restart

State directory

~/.cache/scalafmt-direct/<workspace-hash>/
├── pid           coordinator pid
├── port          loopback port
├── workspace     absolute workspace path
├── log           coordinator stderr
├── calls.log     per-call JSON lines
└── triggers.json mtime baseline

Why no persistent-JVM adapter

The plan originally earmarked a scalafmt-dynamic in-JVM daemon for <300ms warm calls. The native binary beats that floor (0.3-0.8s per call, no JVM boot at all), so the JVM-daemon path would be slower AND strictly more complex. Verdict: closed by "use the native binary"; scalafmt-direct ships it as the primary install option.

If you must route through a scalafmt-dynamic JVM (for example on a platform with no native binary), set SCALAFMT_CMD=cs launch org.scalameta:scalafmt-cli_2.13:<version> -- in the adapter env. Each call pays JVM boot; the adapter has no per-call optimization for this path.