Open Sourcing plx

rust terminal tooling

In the Starship migration post I wrote about building a Rust binary called starship-segments to render powerline-styled prompt segments. It started as a way to match my old powerline-go setup exactly, and ended up 40% faster than both the bash scripts and the Go binary it replaced. The code lived inside my dotfiles repo.

That worked fine for a while. Then I moved the dotfiles to Nix and the binary became a Crane derivation built from a subdirectory. Also fine. But the dotfiles repo was accumulating Rust build artifacts, a Cargo.lock, and test fixtures that had nothing to do with shell configuration. And if someone wanted to use the prompt segments without adopting my entire dotfiles setup, there was no clean way to do that.

So I split it into its own repo and renamed it plx.

What It Does

plx has three subcommands:

plx path renders a powerline-styled working directory. It collapses $HOME to ~, splits the path into components, shows the first on a blue background and the rest on dark grey with thin separators. Deep paths get truncated to five components with an ellipsis.

plx git renders the entire git status as a series of colored segments with proper powerline arrows between them. Green branch for clean repos, pink for dirty. Each status type gets its own segment: staged files on dark green with a checkmark, modified on orange with a pencil, untracked on dark red with a plus, conflicted on bright red, stashed on dark blue. It also shows ahead/behind counts and detects rebase, merge, cherry-pick, and bisect states.

plx tmux-title generates compact tmux window titles. A house emoji for home, a folder for regular directories, and a branch icon with repo name and dirty indicator for git repos. The output uses tmux color codes instead of ANSI escapes.

The key design decision is that all git operations go through libgit2 via the git2 crate. No subprocess calls. No git status, no git branch, no git stash list. Just library calls. That’s where the speed comes from. The git subcommand runs in about 25ms compared to 52ms for an equivalent bash script that shells out to git.

376 Lines

The entire implementation is a single main.rs. I thought about splitting it into modules (path.rs, git.rs, tmux.rs) but it didn’t feel justified. Each subcommand is one function. The shared code is two ANSI color helpers and a few constants. There’s no state, no traits, no abstractions. Just functions that take strings and return strings.

The test suite is another 200 lines in the same file. Tests create temporary git repos with tempfile, stage files, modify them, and assert on the ANSI output. They caught a real bug where conflicted files were being double-counted as both staged and modified.

Building with Nix

The repo has a flake.nix so it builds with nix build or nix run . -- path. My dotfiles consume it as a flake input:

inputs.plx.url = "github:mmichie/plx";

And add it to packages:

home.packages = [ inputs.plx.packages.${system}.default ];

Crane handles the Rust compilation and pins libgit2 in the Nix store. No system-level dependency on libgit2, no Homebrew, no pkg-config at build time. It just works on both macOS and Linux.

You can also build it with cargo build --release if you don’t use Nix. The only dependency is the git2 crate.

Integration

plx is designed to work with Starship custom modules. The config is minimal:

[custom.path_segment]
command = "plx path"
when = "true"
format = "$output"
shell = ["bash", "--nologin"]

[custom.git_segment]
command = "plx git"
when = "true"
format = "$output"
shell = ["bash", "--nologin"]

The format = "$output" is important. It passes raw ANSI escape codes through to the terminal, which is how the colored segments and powerline arrows render correctly. Starship’s built-in modules can’t do the multi-segment color transitions because the format strings are static. They don’t know which segments will be present at render time.

Why Split It Out

Three reasons. First, the dotfiles repo should be config files and Nix modules, not a Rust project with its own build system and test suite. Second, the flake is cleaner as an input than as a subdirectory build. Third, someone might want fast powerline segments without caring about my shell aliases or tmux config.

The name plx doesn’t mean anything in particular. Short, easy to type, not taken on crates.io. Good enough.

The source is at github.com/mmichie/plx.

Discussion