uncomment 3.3.0

A CLI tool to remove comments from code using tree-sitter for accurate parsing
docs.rs failed to build uncomment-3.3.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: uncomment-3.0.3

Strip the noise. Keep the code.

uncomment removes comments from source code using tree-sitter's AST — so it is 100% accurate and never touches comment-like text inside strings. It keeps what matters by default (TODO/FIXME, docs, and linting directives) across 300+ languages, with parallel processing and a safe dry-run mode.

AST-accurate · 306 languages · zero false positives · smart preservation · parallel · dry-run

crates.io npm PyPI CI License: MIT Sponsor

Install · Features · Usage · Configuration · How it works · Contributing


Why uncomment

Regex-based comment strippers guess. They delete a // inside a string literal, mangle a URL in a docstring, or leave a linting directive your CI depends on. uncomment doesn't guess: it parses your code into a real syntax tree and removes only the nodes that are genuinely comments.

Originally built to clean up AI-generated code drowning in explanatory comments, it now works on anything with a tree-sitter grammar.

Features

  • 100% accurate — tree-sitter AST parsing identifies comments structurally, not by pattern matching
  • No false positives — never removes comment-like content from strings
  • Smart preservation — keeps TODO/FIXME, docs, and language-specific linting directives by default
  • 306 languages — powered by tree-sitter-language-pack, grammars downloaded on demand
  • Parallel — multi-threaded processing that scales across cores
  • Safe — dry-run mode with line-by-line diffs previews every change before you write
  • Configurable — hierarchical TOML config with a smart init command
  • Built-in benchmarking — optional performance analysis and profiling tools

Installation

Channel Command
Homebrew (macOS/Linux) brew tap goldziher/tap && brew install uncomment
Cargo (Rust) cargo install uncomment
npm (Node.js) npm install -g uncomment-cli
pip (Python) pip install uncomment

Run without installing:

npx -y uncomment-cli@latest .
uvx uncomment .

Add --dry-run to preview changes before writing.

git clone https://github.com/Goldziher/uncomment.git
cd uncomment
cargo install --path .

Requires Rust 1.70+. npm and pip packages download pre-built binaries automatically.

Quick Start

# Generate a configuration file tuned to your project
uncomment init

# Remove comments from a directory
uncomment src/

# Preview changes as a diff, write nothing
uncomment src/ --dry-run --diff

Usage

# Single file
uncomment file.py

# Multiple files / globs
uncomment src/*.py

# Also strip doc comments and docstrings
uncomment --remove-doc file.py

# Also remove TODO and FIXME comments (preserved by default)
uncomment --remove-todo --remove-fixme file.py

# Add custom patterns to preserve
uncomment --ignore "HACK" --ignore "WARNING" file.py

# Process an entire tree with all CPU cores
uncomment . -j 0

Run uncomment --help for the full, grouped list of options.

The init command detects the languages in your project and writes a matching .uncommentrc.toml:

# Smart detection — includes only the languages it finds
uncomment init

# All 49 built-in languages
uncomment init --comprehensive

# Interactive selection
uncomment init --interactive

# Custom output location / overwrite
uncomment init --output config/uncomment.toml --force

Development binaries for benchmarking and profiling are gated behind the bench-tools feature so they are not installed for regular users:

# Install with extras
cargo install uncomment --features bench-tools

# Or run locally
cargo run --release --features bench-tools --bin benchmark -- --target /path/to/repo --iterations 3
cargo run --release --features bench-tools --bin profile -- /path/to/repo

Supported Languages

uncomment ships with 49 built-in language configurations and can process any of the 306 languages in tree-sitter-language-pack — grammars are downloaded automatically on first use, and any language can be added via configuration.

Python (.py, .pyw, .pyi, .pyx, .pxd) · JavaScript (.js, .jsx, .mjs, .cjs) · TypeScript (.ts, .tsx, .mts, .cts, .d.ts) · Rust (.rs) · Go (.go) · Java (.java) · C (.c, .h) · C++ (.cpp, .cc, .cxx, .hpp, .hxx) · C# (.cs) · Ruby (.rb, .rake, .gemspec) · PHP (.php, .phtml) · Elixir (.ex, .exs) · TOML (.toml) · JSON (.json) · JSON with Comments (.jsonc) · YAML (.yml, .yaml) · HCL/Terraform (.hcl, .tf, .tfvars) · Makefile (Makefile, .mk) · Shell/Bash (.sh, .bash, .zsh) · Haskell (.hs, .lhs) · HTML (.html, .htm, .xhtml) · CSS (.css) · XML (.xml, .xsd, .xsl, .xslt, .svg) · SQL (.sql) · Kotlin (.kt, .kts) · Swift (.swift) · Lua (.lua) · Nix (.nix) · PowerShell (.ps1, .psm1, .psd1) · Protobuf (.proto) · INI-like configs (.ini, .cfg, .conf) · Dockerfile (Dockerfile) · Scala (.scala, .sc) · Dart (.dart) · R (.r, .R) · Julia (.jl) · Zig (.zig) · Clojure (.clj, .cljs, .cljc, .edn) · Elm (.elm) · Erlang (.erl, .hrl) · Vue (.vue) · Svelte (.svelte) · SCSS (.scss) · LaTeX (.tex, .sty, .cls) · Fish (.fish) · Perl (.pl, .pm) · Groovy (.groovy, .gradle) · OCaml (.ml, .mli) · Fortran (.f90, .f95, .f03, .f08)

Preservation Rules

Certain comments are never removed by default — uncomment protects the ones your tooling and teammates rely on.

Always preserved:

  • Comments containing ~keep
  • TODO (unless --remove-todo), FIXME (unless --remove-fixme)
  • Documentation comments (unless --remove-doc)
Language Directives
Go //nolint, //golangci-lint, //staticcheck, //go:generate
Python # noqa, # type: ignore, # mypy:, # pyright:, # ruff:, # pylint:, # flake8:, # fmt: off/on, # black:, # isort:, # bandit:, # pyre-ignore
JS/TS eslint-disable*, @ts-ignore, @ts-expect-error, @ts-nocheck, /// <reference, prettier-ignore, biome-ignore, deno-lint-ignore, v8/c8/istanbul ignore
Rust #[allow], #[deny], #[warn], #[forbid], #[cfg], clippy::, #[rustfmt::skip]
Java @SuppressWarnings, @SuppressFBWarnings, //noinspection, // checkstyle:
C/C++ // NOLINT, // NOLINTNEXTLINE, #pragma, // clang-format off/on
Shell # shellcheck disable, # hadolint ignore
YAML # yamllint disable/enable
HCL/Terraform # tfsec:ignore, # checkov:skip, # trivy:ignore, # tflint-ignore
Ruby # rubocop:disable/enable, # reek:, # standard:disable/enable

Configuration

uncomment reads a hierarchical TOML configuration, merged highest-to-lowest precedence:

  1. Command-line flags
  2. Local .uncommentrc.toml (closest to the file being processed wins)
  3. Global ~/.config/uncomment/config.toml
  4. Built-in defaults
[global]
remove_todos = false
remove_fixme = false
remove_docs = false
preserve_patterns = ["IMPORTANT", "NOTE", "WARNING"]
use_default_ignores = true
respect_gitignore = true

[languages.python]
extensions = ["py", "pyw", "pyi"]
preserve_patterns = ["noqa", "type:", "pragma:", "pylint:"]

[patterns."tests/**/*.py"]
# Keep all comments in test files
remove_todos = false
remove_fixme = false
remove_docs = false

Any of the 306 tree-sitter-language-pack languages works — grammars download automatically on first use, no manual grammar setup:

[languages.hare]
name = "Hare"
extensions = ["ha"]
comment_nodes = ["comment"]
preserve_patterns = ["TODO", "FIXME"]

How It Works

Unlike regex-based tools, uncomment builds a proper Abstract Syntax Tree of your code with tree-sitter, so it distinguishes:

  • Real comments vs comment-like content in strings
  • Documentation comments vs regular comments
  • Inline comments vs standalone comments
  • Language-specific metadata that must be preserved

The pipeline is modular: a language registry (49 built-ins + on-demand grammars) feeds an AST visitor that finds comment nodes, a preservation engine decides what to keep, and an output generator emits clean code.

Git Hooks

repos:
  - repo: https://github.com/Goldziher/uncomment
    rev: v3.3.0
    hooks:
      - id: uncomment
pre-commit:
  commands:
    uncomment:
      run: uncomment {staged_files}
      stage_fixed: true

Performance

AST parsing costs a little more than regex, but the tool is fast and scales well with threads.

  • Small files (<1000 lines): ~20-30ms
  • Large files (>10000 lines): ~100-200ms
Threads Files/second Speedup
1 1,500 1.0×
4 3,900 2.6×
8 5,100 3.4×

Benchmarked on a large enterprise codebase of ~5,000 mixed-language files. Measure your own with the built-in benchmark and profile tools (see optional benchmarking tools).

Development

cargo build              # Debug build
cargo test               # Run the test suite
cargo test -- --ignored  # Include network-dependent tests
cargo clippy             # Lint
cargo fmt --all          # Format

See CONTRIBUTING.md for local development, automation hooks, and release procedures.

Contributing

Issues and pull requests are welcome. If uncomment is useful to you, consider sponsoring development — it helps keep the project maintained for the community.

License

MIT