Code
Last updated: 2026
hledger is a suite of applications, tools and libraries.
The main hledger code repository is github.com/hledgerorg/hledger
(shortcut url code.hledger.org).
There are also various hledger scripts and add-ons maintained as separate projects with their own repos.
hledger packages
Within the main repo, there are a number of separate cabal packages, making it easier to pick and choose parts of hledger to install or to package. They are:
hledger-lib
Core data models, parsing, standard reports, and utilities. Most data types are defined in Hledger.Data.Types, while functions that operate on them are defined in Hledger.Data.TYPENAME. Under Hledger.Read are parsers for the supported input formats. Data files are parsed into a Journal, which contains a list of Transactions, each containing multiple Postings of some MixedAmount (multiple single-CommoditySymbol Amounts) to some AccountName. When needed, the Journal is further processed to derive a Ledger, which contains summed Accounts. In Hledger.Reports there are standard reports, which extract useful data from the Journal or Ledger.
Here’s a diagram of the main data model:
The main modules:
- Hledger.Data: Types, Amount, Posting, Transaction, Journal, Account, Balancing, Lots, Valuation, Dates, Period, Errors..
- Hledger.Read: JournalReader, CsvReader (with RulesReader), TimeclockReader, TimedotReader
- Hledger.Query: the query language, used for filtering in reports and UIs
- Hledger.Reports: ReportOptions, BalanceReport, MultiBalanceReport, PostingsReport, BudgetReport, EntriesReport..
- Hledger.Write: output formats: Csv, Html, Ods/Spreadsheet, Beancount, Ledger
- Hledger.Utils: Debug, Parse, Regex, String, Text, IO, Test helpers
- Hledger
re-exports all of the above, so most modules just
import Hledger.
hledger
hledger’s command line interface, and command line options and utilities for other hledger tools.
The main modules:
- Hledger.Cli.CliOptions: command line options and their parsing (using cmdargs)
- Hledger.Cli.Commands: one module per command, listed in Commands.hs; the balancesheet/incomestatement/cashflow commands share CompoundBalanceCommand
- Hledger.Cli.Utils: reading the journal and other helpers for commands
- Hledger.Cli.Conf: config files
- Hledger.Cli.Script: a convenience module to import in hledger scripts
Each command is a module Somecommand.hs and a doc Somecommand.md in Commands/.
The doc is converted to Somecommand.txt, which is embedded in the module to provide hledger somecommand --help,
and also included in the hledger manual.
See Commands/README.md.
Try tracing the execution of a hledger command:
- Hledger.Cli:main parses the command line to select a command, then
- gives it to Hledger.Cli.Utils:withJournal, which runs it after doing all the initial parsing.
- Parsing code is under hledger-lib:Hledger.Read, eg Hledger.Read.JournalReader.
- Commands extract useful information from the parsed data model using hledger-lib:Hledger.Reports, and
- render in plain text for console output (or another output format, like CSV).
- Everything uses the data types and utilities from hledger-lib:Hledger.Data and hledger-lib:Hledger.Utils.
A suggested reading order for newcomers: Hledger.Data.Types for the data model, Hledger.Read.JournalReader for how journals are parsed, a simple command like Commands/Accounts.hs or Commands/Balance.hs, and then the report it calls in Hledger.Reports.
hledger-ui
A terminal interface, built with brick and vty.
It reads the journal file(s) at startup and, with --watch, again whenever they change.
Screens are kept on a stack, so that going into an account register or a transaction and back out restores the previous view.
The main modules, all under Hledger/UI:
- Main: startup, the brick app definition, file watching and date change detection
- UIOptions: command line options
- UITypes:
the app state (
UIState), theScreentype and each screen’s state type, widget names - UIState: app state updates: toggling filters, depth, tree mode, lot detail, etc.
- UIScreens: constructors and updaters for all screens, gathered here so any screen can (re)generate the others
- MenuScreen, AccountsScreen, RegisterScreen, TransactionScreen, ErrorScreen: each screen’s drawing and event handling. AccountsScreen serves the all accounts, cash, balance sheet and income statement screens.
- UIUtils: shared drawing and key handling helpers, the help dialog, suspend/resume, warning collection
- Editor: launching an external editor at a file position
- Theme: the colour themes
Manual test procedures are in test/uitest.md.
hledger-web
A web interface. hledger-web starts a web server built with the yesod framework, and (by default) opens a web browser view on it. It reads the journal file(s) at startup and again whenever they change. It can also write (append) new transactions to the journal file. The views, sidebar, add form and keyboard shortcuts are described in the manual’s WEB UI section.
Most of the action is in
- config/routes
- templates/default-layout-wrapper.hamlet
- Hledger/Web/App.hs (the yesod foundation type)
- Hledger/Web/Handler/*
- Hledger/Web/Widget/*
- static/hledger.js
- static/hledger.css
Handler module and function names end with R, like the yesod-generated route type they deal with.
Dynamically generated page content is mostly inline hamlet. Lucius/Julius files and widgets generally are not used, except for the default layout.
For ways to run it during development, see Run hledger-web in development.
Conventions
- Functions for a type in Hledger.Data.Types live in Hledger.Data.TYPENAME (Amount, Posting, Journal, ..).
- Module name suffixes:
*Readerparses an input format,*Reportcomputes a report,*Optionsdeclares command line options; in hledger-web,*Ris a route handler. - Most modules
import Hledger, which re-exports the hledger-lib API. - Tags whose names begin with
_are hidden tags for internal use;printshows them only with--verbose-tags. - Code style: post-qualified imports (
import Data.Map qualified as M), and the -Wall variant shown in DEVWORKFLOWS. - Keep imports compatible with the oldest supported GHC (see
tested-with:in hledger-lib/package.yaml).
Debug output
The main hledger programs accept --debug[=N] (N from 1 to 9), which prints debug output on stderr
(hledger-ui logs it to hledger-ui.log instead, since it is redrawing the screen).
In code, use the dbg0..dbg9 helpers from
Hledger.Utils.Debug,
eg dbg4 "report" x prints a label and the pretty-printed value when the debug level is 4 or more.
Levels are used roughly as: 0 for unconditional logging during development, 1 warnings and common troubleshooting,
3-5 report options and generation, 6-7 input file reading, 8 command line parsing, 9 anything else; see the module’s haddock.
Tests
hledger has unit tests, functional tests, doctests, performance tests, installation tests, and browser tests for hledger-web (README). See TESTS for the kinds of tests and their coverage, and DEVWORKFLOWS for how to run them.
Haddock comments
hledger is a documentation-driven project, and this is one reason it has held together and kept improving for so long. (More discussion in #2222.)
Haddock comments are used pervasively in the hledger codebase, and they are considered when reviewing code. There is no hard requirement, but most definitions should have at least a line of english description. The bar can be lower in single-purpose, rarely-changed modules like FODS.hs, and obviously redundant haddocks that add no value should be avoided.
Why: haddocks anchor the developer while writing or changing code; they are the cheapest kind of doc, spec and test suite, and a place to add doctests later; they help contributors who are not expert haskellers, and experienced developers returning to code months or years later. They also help AI tools work effectively on the hledger codebase.