injm
A CLI tool that injects content into marked regions in source files.
Table of Contents
Installation
Cargo
Nix
Download Binary
Download the latest binary for your platform from GitHub Releases.
Usage
Quick Start
The simplest way is to create an injm.toml in your project root and run injm without any subcommand:
= ["src/**/*.rs"]
= ["docs/"]
= ["target/**"]
This reads content from src/**/*.rs, finds all <id markers, and injects them into matching >id regions in docs/.
You can also use subcommands for one-off operations. The main one is inject:
Basic Injection
Mark a region in your source file with injm begin and injm end comments:
dest.rs
Then pipe content into injm:
|
Result:
dest.rs
Running injm inject again will replace the content between the markers:
|
Inject into a Specific Region
Give a region an output ID with >id, then target it with --id:
dest.rs
Inject into a specific region:
|
Inject into multiple regions at once:
|
If --id is not specified, only regions without an ID are injected; regions with a >id are left untouched.
Sync Between Files
Instead of piping from stdin, copy content between files with --input.
Mark the source region with <id (the content to read) and the destination
region with >id (where it goes):
src.rs
dest.rs
Then sync:
dest.rs becomes:
A region may read from several sources by listing multiple <id markers.
If a >id in the output has no matching <id in the input, injm reports
the missing ID and exits with an error.
Marker Region Configuration
Per-region options can be appended to the injm begin marker as :option=value
tokens. They are set on the output region (the >id marker) and applied to
whatever content is injected into it. Multiple options may be combined in any
order:
// injm begin >id :offset=1 :trim=true :indent=4
// injm end
:offset=N (default 0) — extend the region being replaced by N lines beyond the markers on each side, so lines just inside the markers survive injection. Useful for wrapper lines:
dest.tex
% injm begin >id :offset=1
% injm end
Only the content between \begin{minted} and \end{minted} is replaced; the wrapper lines are kept. An offset that exceeds the block's own content reports an error.
:trim / :trim=true (default false) — remove leading and trailing blank lines from the injected content:
// injm begin >id :trim=true
// injm end
:indent=N (default unset) — rebase the minimum indentation of the injected
content to N columns, preserving the relative indentation of the other lines.
The example below injects an 8-column-indented region at 4 columns instead:
src.rs
dest.rs
Becomes:
Multiple Files and Globs
--input and --output accept multiple values and glob patterns:
# Multiple explicit files
# Sync to multiple outputs
# Glob patterns
# Multiple globs
Excluding Files
Skip files with --exclude (or -e). Accepts glob patterns matched against
absolute paths:
# Exclude a specific file
# Exclude with glob patterns
# Multiple flags
Exclusion applies to both --input and --output files. The same patterns can also be set in injm.toml (see Project Configuration).
.gitignore Integration
By default, injm respects .gitignore rules — any file matched by .gitignore is skipped:
# Files in .gitignore are automatically excluded
To include gitignored files, use --no-gitignore:
Project Configuration
Create an injm.toml in your project root to define input sources, output
destinations, and exclusion patterns:
= ["examples/*.md"]
= ["docs/**"]
= [
"target/**",
"vendor/**"
]
When injm.toml is present, you can run injm without any subcommand:
This reads the config, injects content from input into matching regions in
output, and writes the result. Equivalent to:
You can also point to a different config file with --config:
Config values are merged with CLI flags — CLI arguments take precedence:
# Merges config's output with --exclude from CLI
List Markers
Preview all marker regions across files:
Output:
+-------------+----------+--------+-------+
| File | ID | Type | Lines |
+-------------+----------+--------+-------+
| src/main.rs | hello | output | 6-7 |
+-------------+----------+--------+-------+
| src/main.rs | hello | input | 23-24 |
+-------------+----------+--------+-------+
| src/cli.rs | greeting | input | 1-2 |
+-------------+----------+--------+-------+
JSON output:
Accepts positional arguments (files, globs, or directories). Falls back to current directory when no argument is given.
Dry Run
Preview the result without writing to the file:
|
To see a unified diff of what would change instead of the full file, add --diff:
|
These flags also work with the root command when using injm.toml:
# Preview all changes from config
# Show unified diff of what would change
Check
Verify that all output blocks (>id) contain the same content as their matching input blocks (<id):
If all blocks are synchronized, injm check exits 0 and prints:
all marker blocks are synchronized
If any are out of sync, it exits non-zero and lists each mismatch:
src/main.rs:12-14: output block `hello` is out of sync
To see a unified diff of what each out-of-sync block should contain, use
--diff:
Accepts files, globs, or directories as arguments. Falls back to current directory when no argument is provided.
Supported Languages
injm uses tree-sitter to parse source files, so markers are detected from actual comment nodes — not from string literals or other non-comment content.
Supports any language recognized by tree-sitter-language-pack, including:
- Rust, C, C++
- Python, Ruby
- JavaScript, TypeScript
- Go, Java
- And 300+ more
Contributing
See CONTRIBUTING.md.
Roadmap
See ROADMAP.md.
License
MIT
Acknowledgement
- clap-rs/clap: A full featured, fast Command Line Argument Parser for Rust.
- ignore: The ignore crate provides a fast recursive directory iterator that respects various filters such as globs, file types and .gitignore files. This crate also provides lower level direct access to gitignore and file type matchers.
- rust-lang/glob: Support for matching file paths against Unix shell style patterns.
- serde-rs/serde: Serialization framework for Rust.
- xberg-io/tree-sitter-language-pack: Comprehensive tree-sitter grammar compilation with polyglot bindings — Rust, Python, Node.js, Go, Java, Ruby, Elixir, PHP, C#, WASM, Dart, Kotlin-Android, Swift, Zig, and CLI. 306+ languages.
- zhiburt/tabled: An easy to use library for pretty print tables of Rust structs and enums.