hledger-lsp
v0.4.8
Published
Language Server Protocol implementation for hledger plain text accounting
Maintainers
Readme
hledger Language Server
A Language Server Protocol implementation for hledger plain text accounting; bringing IDE-grade tooling to your journal files.
Completion, validation, formatting, navigation, semantic highlighting, inlay hints, and more — in any editor that supports LSP.
Editor plugins: VS Code · Neovim · Any LSP client
- hledger Language Server
Installation
npm install -g hledger-lspThis provides the hledger-lsp command globally. After installation, the
language server can be used with any LSP-compatible editor.
Command Line Usage
The hledger-lsp command can be used in two modes:
LSP Server Mode (default)
hledger-lsp --stdioThis starts the language server for use with LSP-compatible editors.
Standalone Formatter
Format journal files directly from the command line without an IDE:
# Format a file and output to stdout
hledger-lsp --format myfile.journal
# Format a file and write to output file
hledger-lsp --format myfile.journal -o formatted.journal
hledger-lsp --format myfile.journal --output formatted.journal
# Format stdin and write to file
cat myfile.journal | hledger-lsp --format - -o formatted.journal
# Format in place (overwrite original file)
hledger-lsp --format myfile.journal -o myfile.journalThe formatter applies default formatting settings:
- 4-space indentation for postings
- Decimal point alignment at column 52
- Normalized transaction header spacing
Other Options
hledger-lsp --help # Show help message
hledger-lsp --version # Show versionIDE Integration
This language server works with any LSP-compatible editor. First-party plugins are available for VS Code and Neovim:
| Editor | Plugin | Install | |---------|----------------|----------------------------------| | VS Code | hledger-vscode | VS Code Marketplace | | Neovim | hledger-nvim | Plugin manager (lazy.nvim, etc.) |
Both plugins provide all LSP features with zero configuration. The VS Code extension also includes a workspace graph tree view; the Neovim plugin includes a workspace graph floating window.
Other Editors
The server can be used with any LSP client. Example configuration:
{
"command": "hledger-lsp",
"args": ["--stdio"],
"filetypes": ["hledger", "journal"],
"rootPatterns": [".hledger-lsp.json", ".git"]
}Features
Comprehensive IDE support for hledger journal files, including regular
transactions, periodic transactions (~), and auto posting rules (=):
Code Completion
- Account names - Auto-complete from accounts (configurable: declared only or all)
- Payees - Complete transaction descriptions from payees (configurable: declared only or all)
- Commodities - Auto-complete currency symbols (configurable: declared only or all)
- Tags - Complete tag names in comments (configurable: declared only or all)
- Directives - Complete hledger directives (account, commodity, payee, tag, include, alias)
- Include paths - File path completion for include directives
- Smart completions - Context-aware account suggestions based on payee history (learns from your transaction patterns)
- Smart filtering - By default, only declared items appear in completions (can be configured to include all items)
Hover
- Hover information - Lightweight hover support for accounts, payees and dates. Hover text is provided by the language server and can be extended to show declarations, balances and commodity formats.
Validation
- Transaction balance - Verify transactions balance to zero per commodity
- Missing amounts - Ensure at most one posting omits an amount
- Undeclared items - Warn about undeclared accounts, payees, commodities, and tags
- Date ordering - Detect out-of-order transactions
- Balance assertions - Verify balance assertions match calculated balances
- Empty transactions - Require at least 2 postings per transaction
- Date validation - Check for invalid dates (e.g., Feb 30, month 13)
- Future dates - Warn about future-dated transactions
- Empty descriptions - Warn about transactions with no description
- Include files - Detect missing include files
- Circular includes - Detect circular include dependencies
- Periodic transactions - Balance and missing-amount checks for
~periodic transaction rules (same rules as regular transactions) - Auto postings - Undeclared account/commodity checks for
=auto posting rules (balance checks are skipped since auto postings are partial by design)
Include Directive Support
- Multi-file journals - Parse and merge multiple journal files via include directives
- Relative and absolute paths - Support both path styles, including
../parent directory references - Tilde expansion - Support for
~/home directory paths - Paths with spaces - Properly handles file paths containing spaces and special characters (e.g., "Cloud Storage", "My Documents (2025)")
- Circular detection - Prevent infinite loops from circular includes
- Caching - Efficient parsing with include file caching
- Dependency tracking - Auto-revalidate files when included files change
- Source tracking - Track which file each entity originates from
Navigation
- Document symbols - Outline view showing directives, transactions, periodic
transactions (
~), and auto posting rules (=) with their postings - Workspace symbols - Project-wide search across all accounts, payees, commodities, tags, and transactions
- Go to definition - Jump to declarations for accounts, payees, commodities, and tags
- Find references - Show all usages of accounts, payees, commodities, or tags across the document
Code Actions and Quick Fixes
- Add declarations - Automatically add account, payee, commodity, or tag declarations for undeclared items
- Smart insertion - Directives are inserted in appropriate locations, grouped with similar directive types
- Rename refactoring - Rename accounts, payees, commodities, or tags across all their references in the document
- Context-aware actions - Actions appear when cursor is positioned on renameable items
Formatting
- Document formatting - Format entire journal files with consistent indentation and spacing
- Range formatting - Format selected portions of your journal
- On-type formatting - Auto-indent postings when pressing Enter after transaction headers
- Decimal-point alignment - Automatically align amounts by their decimal point (or implied decimal position for whole numbers) within transactions, making it easy to visually compare amounts
- Negative amount support - Properly aligns negative amounts regardless of
minus sign position (e.g.,
-$100.00or$-100.00) - Commodity format support - Reformats amounts according to declared commodity formats (symbol placement, decimal separators, thousands separators, precision)
- Precision preservation - Maintains decimal precision even when commodity format specifies fewer decimal places
- Configurable - Customize indentation width, alignment column, minimum spacing, trailing whitespace handling, and more
Semantic Highlighting
- Context-aware syntax highlighting - Rich semantic highlighting based on token type and meaning
- Token types - Distinct highlighting for dates, accounts, payees, commodities, tags, directives, amounts, comments, and status indicators
- Token modifiers - Additional styling for declarations (directive definitions) and readonly items (dates, amounts)
- Hierarchical accounts - Account names highlighted as namespaces to reflect their hierarchical nature
- Tag detection - Automatic highlighting of tags within comments (key:value pairs)
- Periodic & auto postings -
~and=operators, period expressions, queries, and their postings are all semantically highlighted
Inlay Hints
- Inferred amounts - Show calculated amounts for postings without explicit amounts (including periodic transaction postings)
- Running balances - Display running balances per account and commodity after each posting
- Cost conversions - Show total cost in target commodity for postings with cost notation (@ or @@)
- Configurable - Enable/disable each hint type independently
- Range-aware - Only show hints for visible portions of the document for better performance
Code Lens
- Balance assertions - Show running balances on each posting line in balance
assertion format (
= $50.00) - Clickable - Click a balance assertion code lens to insert it into the posting
- Transaction counts - Display how many transactions each account has been involved in on transaction headers
- Configurable - Enable/disable each lens type independently
Editor Integration
- Folding ranges - Collapse/expand transactions, periodic transactions, and auto posting rules to hide postings; fold multi-line comment blocks
- Document links - Clickable include paths that open the referenced file (supports relative and absolute paths)
- Selection range - Smart text selection expansion: Word → Account → Posting → Transaction (requires editor keybinding setup, see configuration below)
Configuration
All validations and completion behaviors can be individually configured per workspace or document:
- Validation settings - Enable/disable individual validation rules and customize severity levels
- Completion filtering - Control whether completions show only declared items or all items (declared + undeclared)
General Settings
maxNumberOfProblems(number, default: 1000): Maximum number of diagnostic problems to report per filehledgerPath(string, default: "hledger"): Path to the hledger executable (reserved for future CLI integration)
Validation Settings
Most validation settings default to true and can be individually disabled:
validation.balance(default: true): Verify transactions balance to zero per commodityvalidation.requireExplicitCosts(default: false): Require explicit@or@@cost notation for multi-commodity transactions. When disabled (default), the LSP auto-infers costs like hledger'sautobalancedmode. When enabled, mirrors hledger's stricterbalancedcheck.validation.missingAmounts(default: true): Ensure at most one posting per transaction omits an amountvalidation.undeclaredAccounts(default: true): Warn about accounts used but not declaredvalidation.undeclaredPayees(default: false): Warn about payees used but not declaredvalidation.undeclaredCommodities(default: true): Warn about commodities used but not declaredvalidation.undeclaredTags(default: false): Warn about tags used but not declaredvalidation.dateOrdering(default: true): Detect transactions with dates out of chronological ordervalidation.balanceAssertions(default: true): Verify balance assertions match calculated balancesvalidation.emptyTransactions(default: true): Require at least 2 postings per transactionvalidation.invalidDates(default: true): Check for invalid dates (e.g., February 30, month 13)validation.futureDates(default: true): Warn about future-dated transactionsvalidation.emptyDescriptions(default: true): Warn about transactions with no descriptionvalidation.formatMismatch(default: true): Validate that amounts match their commodity format directivesvalidation.includeFiles(default: true): Detect missing include filesvalidation.circularIncludes(default: true): Detect circular include dependenciesvalidation.markAllUndeclaredInstances(default: true): Mark all instances of undeclared resources with diagnostics, not just the first occurrence
Severity Settings
Customize the severity level for undeclared item warnings. Options: "error",
"warning", "information", "hint"
severity.undeclaredAccounts(default: "warning")severity.undeclaredPayees(default: "warning")severity.undeclaredCommodities(default: "warning")severity.undeclaredTags(default: "information")
Include Settings
include.followIncludes(boolean, default: true): Parse and merge included journal filesinclude.maxDepth(number, default: 10): Maximum include depth to prevent infinite recursion
Workspace Settings
Enable workspace-aware parsing for features that need global context (running balances, completion, validation):
workspace.enabled(boolean, default: true): Enable workspace-aware parsing. When enabled, the server discovers all journal files in your workspace, builds an include graph, and identifies a single root file. This allows features to access workspace-wide state even when working with "leaf" files that don't include other files.workspace.eagerParsing(boolean, default: true): Parse all discovered files eagerly on startup. If disabled, files are parsed on-demand.workspace.autoDetectRoot(boolean, default: true): Automatically detect the root file using heuristics (prefers files with no parents that include many others, with names like "main", "all", or "index"). If disabled, only an explicitly configured root file is used (see Configuration File Support below).
Configuration File Support:
You can create a .hledger-lsp.json file in your workspace to explicitly
configure workspace behavior:
{
"rootFile": "main.journal",
"include": ["**/*.journal", "**/*.hledger"],
"exclude": ["**/archive/**", "**/temp/**"],
"workspace": {
"enabled": true,
"eagerParsing": true,
"autoDetectRoot": false
}
}Settings:
rootFile(string): Explicit root file path (relative to config file location)include(array of glob patterns): File discovery patterns (default:["**/*.journal", "**/*.hledger"])exclude(array of glob patterns): Files to exclude (default:["**/node_modules/**", "**/.git/**", "**/.*"])workspace(object): Same workspace settings as above
Important: The .hledger-lsp.json file is ONLY for workspace structure
configuration (which files to discover, which are roots, etc.).
LSP feature settings like inlay hints, code lens, validation rules,
and formatting preferences should be configured in your editor/IDE
settings (VS Code settings.json, Neovim LSP config, etc.).
The configuration file will be automatically discovered by walking up the directory tree from your journal files. Settings from VS Code/editor configuration override settings from the config file.
Performance Tips:
- For large workspaces (>100 files), use
excludepatterns to skip unnecessary files - Disable
eagerParsingif initialization is slow - Check LSP server logs for performance warnings and metrics
Completion Settings
Control which items appear in auto-completion suggestions. All settings default
to true (only show declared items):
completion.onlyDeclaredAccounts(boolean, default: true): Only show accounts declared withaccountdirective in completionscompletion.onlyDeclaredPayees(boolean, default: true): Only show payees declared withpayeedirective in completionscompletion.onlyDeclaredCommodities(boolean, default: true): Only show commodities declared withcommoditydirective in completionscompletion.onlyDeclaredTags(boolean, default: true): Only show tags declared withtagdirective in completions
Set to false to include items that are used but not explicitly declared in
completions.
Formatting Settings
Configure document formatting behavior:
formatting.indentation(number, default: 4): Number of spaces for posting indentationformatting.maxCommodityWidth(number, default: 4): Maximum width allocated for commodity symbolsformatting.maxAmountIntegerWidth(number, default: 12): Maximum width allocated for the integer part of amount numbersformatting.maxAmountDecimalWidth(number, default: 3): Maximum width allocated for the decimal part of amount numbersformatting.minSpacing(number, default: 2): Minimum number of spaces between account names and amountsformatting.decimalAlignColumn(number, default: 52): Target column position for aligning decimal points in amountsformatting.assertionDecimalAlignColumn(number, default: 70): Target column position for aligning decimal points in balance assertionsformatting.signPosition(string, default: "after-symbol"): Where to place the negative sign for prefix commodities. Options:"after-symbol"(e.g.,$-100.00) or"before-symbol"(e.g.,-$100.00). This affects how amounts are displayed in hover information, inlay hints, and other LSP features. Does not affect postfix commodities (e.g.,-100.00 EUR), which always show the sign before the number.formatting.showPositivesSign(boolean, default: false): Whether to show a+sign for positive amounts
Inlay Hints Settings
Configure which inline hints to display (running balances is disabled by default as it can make the file busy):
inlayHints.showInferredAmounts(boolean, default: true): Show calculated amounts for postings that omit explicit amountsinlayHints.showRunningBalances(boolean, default: false): Display running balance after each postinginlayHints.showCostConversions(boolean, default: true): Show total cost in target commodity for postings with @ or @@ notation
Code Lens Settings
Configure which code lenses to display (disabled by default, largely a holding for future features):
codeLens.showTransactionCounts(boolean, default: false): Show transaction counts for each account on transaction headers
Development
Prerequisites
- Node.js >= 16.0.0
Building from Source
# Clone the repository
git clone https://github.com/ptimoney/hledger-lsp.git
cd hledger-lsp
# Install dependencies
npm install
# Build the server
npm run build
# Run the server directly
node out/server.js --stdioProject Structure
hledger-lsp/
├── src/
│ ├── server.ts # Main LSP server implementation
│ ├── types.ts # Type definitions for hledger structures
│ ├── parser/ # Journal file parser
│ ├── features/ # LSP feature implementations
│ ├── server/ # Server infrastructure
│ └── utils/ # Utility functions
├── tests/ # Test suite (1380+ test cases)
├── out/ # Compiled JavaScript output
└── package.jsonDevelopment Commands
# Build once
npm run build
# Watch mode (rebuild on changes)
npm run watch
# Lint code
npm run lint
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Generate coverage report
npm run test:coverage
# Run tests with verbose output
npm run test:verbose
# Run specific test file
npx jest tests/parser/index.test.ts
# Run tests matching a pattern
npx jest --testNamePattern="balance"Developing with the VS Code extension
When working on the language server and the VS Code extension together you have two convenient workflows:
Preferred: sibling local build
If you have the
hledger-vscodeextension checked out next to this repo (as siblings), the extension will prefer a local built server at../hledger-lsp/out/server.js.Steps:
# In this repo (language server) cd /path/to/hledger-lsp npm install npm run compile # produces out/server.js # In the extension repo (open this folder in VS Code) cd /path/to/hledger-vscode npm install npm run compileThen open the
hledger-vscodefolder in VS Code and press F5 to launch the Extension Development Host. The extension will detect the localout/server.jsand use it when starting the language client.Alternative:
npm linkIf you prefer to keep the projects separate or want the extension to pick up a locally-installed (linked) package, use
npm link:# In language server repo cd /path/to/hledger-lsp npm link # In extension repo cd /path/to/hledger-vscode npm link hledger-lsp npm install npm run compileThis makes
require.resolve('hledger-lsp/out/server.js')resolve to your local linked package.
Notes and tips
After building the server and extension, use Developer: Reload Window in the extension host or the
hledgerLanguageServer.reloadcommand (provided by the extension) to restart the language client so changes propagate.The server now logs the semantic token legend on initialize. Open View → Output and select the
hledger Language Serveroutput channel to see messages such as:Semantic tokens legend: {"tokenTypes":["namespace","keyword","class","variable","property","type","number","string","comment","operator"],"tokenModifiers":["declaration","readonly","deprecated"]}
Running tests:
- Server tests (in
hledger-lsp):npm test(ornpx jest <path>) - Extension tests (in
hledger-vscode):npm test— these use mockedvscodeandvscode-languageclientmodules so they don't require a real server binary.
- Server tests (in
If you want an explicit server path override, set up a quick environment
variable in your debug launch configuration or use npm link as described above.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
For bug reports and feature requests, open an issue.
Related Projects
| Project | Description | |---------|-------------| | hledger-vscode | VS Code extension — install from the Marketplace | | hledger-nvim | Neovim plugin with LSP integration and workspace graph | | hledger | The plain text accounting tool this server supports | | LSP Specification | The protocol this server implements |
License
MIT
