A fast, extensible grammar and style checker for Markdown, HTML, LaTeX, Typst, and more. Ships as a VS Code extension and Neovim plugin, backed by a Rust core binary with LSP and Protobuf server modes.
Note
Language Check is in early development. Core checking works and is usable day-to-day, but expect rough edges — if you hit a bug, please open an issue and it will be addressed promptly. The configuration format may evolve between minor versions; backwards compatibility is a goal but not yet guaranteed.
| VS Code | Neovim |
![]() |
![]() |
- Multi-engine checking — local Harper engine for instant feedback, optional LanguageTool for deep grammar analysis, optional Vale for style linting with its rich plugin ecosystem, external binaries, and WASM plugins via Extism
- Tree-sitter parsing — extracts prose from Markdown, HTML, LaTeX, Typst, and more while skipping code blocks, math, and markup
- SpeedFix panel — keyboard-driven batch review: press
1–9for suggestions,ato add to dictionary,ito ignore,Spaceto skip - Inline suggestions — inlay hints, ghost text completions, and quickfix code actions
- Standalone CLI —
language-check check,fix,list-rules, andconfigsubcommands - Workspace indexing — background indexing with redb-backed caching
- Rule normalization — unified rule IDs across providers for consistent severity overrides
- Prose insights — word count, sentence count, and Automated Readability Index in the status bar
- Inspector — debug webview showing AST structure, prose extraction ranges, and check latency
- i18n — extension UI localized in English, German, French, and Spanish
- Install the extension from the VS Code marketplace (publisher:
KaiErikNiermann) - Open a Markdown, HTML, LaTeX, or Typst file — checking starts automatically on save
- Press
Alt+Fto open the SpeedFix panel
-- lazy.nvim
{
"KaiErikNiermann/lang-check.nvim",
ft = { "markdown", "html", "latex", "typst", "restructuredtext",
"org", "bibtex", "sweave" },
opts = {},
}
-- Or with Neovim 0.11+ native LSP config:
vim.lsp.enable("lang_check")See editors/nvim/README.md for full setup instructions.
# Build from source
cd rust-core
cargo build --release
# Check a file
./target/release/language-check check README.md
# Auto-fix high-confidence issues
./target/release/language-check fix docs/
# List available rules
./target/release/language-check list-ruleslang-check/
├── rust-core/ Rust core binary (server + CLI)
│ ├── src/
│ │ ├── engines.rs Harper, LanguageTool, External, WASM engines
│ │ ├── orchestrator.rs Multi-engine coordinator
│ │ ├── prose.rs Tree-sitter prose extraction
│ │ ├── rules.rs Rule normalization
│ │ ├── config.rs YAML/JSON configuration loader
│ │ ├── workspace.rs redb-backed workspace indexing
│ │ └── ...
│ └── bin/
│ ├── language-check-server.rs LSP + Protobuf stdio server
│ └── language-check.rs Standalone CLI
├── editors/nvim/ Neovim plugin (lang-check.nvim)
│ ├── lua/lang_check/ Plugin core (config, binary, health)
│ ├── lsp/ Neovim 0.11+ native LSP config
│ └── doc/ Vim help file
├── extension/ VS Code extension (TypeScript)
│ ├── src/
│ │ ├── extension.ts Activation, commands, providers
│ │ ├── client.ts Protobuf IPC client
│ │ ├── api.ts Public API for other extensions
│ │ ├── downloader.ts Core binary auto-download
│ │ └── trace.ts Protobuf message tracing
│ ├── webview/ SpeedFix + Inspector UIs (Svelte 5 + Tailwind)
│ ├── l10n/ Runtime localization bundles
│ └── package.nls.*.json Command/setting translations
├── proto/ Protobuf schema (checker.proto)
├── docs/ Sphinx documentation
├── scripts/ Build utilities
└── docker-compose.yml Local LanguageTool server
Create a .languagecheck.yaml in your workspace root:
engines:
harper: true
languagetool: true
languagetool_url: "http://localhost:8010"
external:
- name: vale
command: /usr/bin/vale
args: ["--output", "JSON"]
extensions: [md, rst]
wasm_plugins:
- name: custom-checker
path: .languagecheck/plugins/checker.wasm
rules:
spelling.typo:
severity: warning
grammar.article:
severity: off
performance:
high_performance_mode: false
debounce_ms: 300
max_file_size: 1048576
auto_fix:
- find: "teh"
replace: "the"See language-check config init to generate a default configuration.
- Rust 2024 edition (1.85+)
- Node.js 18+ and pnpm
- Protobuf compiler (
protoc)
# Rust core
cd rust-core
cargo build
# Extension
cd extension
pnpm install
pnpm run proto:gen
pnpm build# Rust — unit tests + snapshot tests (insta)
cd rust-core
cargo test
# Extension — vitest
cd extension
pnpm test# Rust
cargo clippy -- -D warnings
cargo fmt --check
# TypeScript
cd extension
pnpm lintdocker compose up -d
# LanguageTool API available at http://localhost:8010The project uses Lefthook for pre-push checks (cargo check, clippy, fmt, test, and tsc).
Language Check supports three extension mechanisms:
| Type | Sandboxed | Config key | Protocol |
|---|---|---|---|
| External binary | No | engines.external |
JSON over stdin/stdout |
| WASM plugin | Yes (Extism) | engines.wasm_plugins |
check(input) -> output export |
| VS Code API | N/A | — | LanguageCheckAPI interface |
See docs/advanced/plugins.md for the WASM plugin development guide.
Full documentation is built with Sphinx:
cd docs
pip install -r requirements.txt
make html
# Open _build/html/index.html
