Commit graph

35 commits

Author SHA1 Message Date
John Kerl
de013dc35c
Right-align headers over all-numeric columns with --right-align-numeric (#380) (#2167)
With --right-align-numeric, PPRINT data cells right-align but headers
stayed left-aligned, so a header did not line up with its own column's
data -- the original ask in #380. Now a header is right-aligned when
every value in its column is numeric, for both non-barred and barred
PPRINT output. Mixed columns keep left-aligned headers.

For --omd-aligned, the raw header text of right-aligned columns is now
right-justified too, matching how Markdown viewers render the ---:
marker; this follows the same all-values-numeric per-column rule
already used for the separator markers.

Man-page regeneration also picks up previously-merged reorder help-text
edits that had not been regenerated.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:45:14 -04:00
John Kerl
fdcca41536
Add --right-align-numeric for PPRINT and Markdown output (#1503) (#2161)
For PPRINT, right-justifies data cells whose values are numeric,
leaving other cells and header lines left-justified. For Markdown,
emits right-alignment markers (---:) for numeric columns; in
--omd-aligned mode also right-justifies the raw cell text.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:02:18 -04:00
John Kerl
ac08b072dc
Honor --ors crlf for CSV output (#1810) (#2150)
* Honor --ors CRLF for CSV output (#1810)

The full-CSV record writer validated ORS as newline or
carriage-return/newline, but never propagated the choice to the forked
Go CSV writer's UseCRLF field, so `--ors '\r\n'` (or `--ors crlf`)
silently produced LF line endings. Set UseCRLF from the writer options
so CRLF output is honored. Default behavior (LF) is unchanged, and
other ORS values are still rejected. The CSV-lite and TSV writers
already honored CRLF ORS.

Also: fix a copy-paste "for CSV" in the TSV writer's ORS-validation
message, add unit tests asserting byte-exact line endings (the regtest
harness normalizes CR/LF, so this can't be asserted in test/cases), add
CLI-level regression cases, and update the separators documentation.

This substantially addresses #1722 as well: RFC-4180-style CRLF output
can now be requested on any platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Fix errcheck lint: check Flush() error in CSV writer test

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:35:32 -04:00
John Kerl
41f5188bd0
Add mlr mcp MCP server + agent playbook, with a --no-shell gate (#2098 PR7) (#2133)
* Plan: flesh out PR7 (MCP server + Agent Skill) design

stdio transport (no HTTP port), mlr mcp terminal in the main binary,
SDK-vs-handroll decision, tool list, in-process vs subprocess split,
run-tool safety (--no-shell prerequisite), single-sourced skill, tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Add mlr mcp: MCP server + agent playbook; --no-shell gate (#2098 PR7)

New terminal `mlr mcp` runs a Model Context Protocol server over stdio
(spawned by MCP clients; no network port), exposing five tools --
list_capabilities, which, validate_dsl, describe_data, run -- plus an
agent playbook as MCP prompt/resource. Catalog tools are served
in-process from the help registries; the rest subprocess this same
binary with MLR_ERRORS_JSON=1, a timeout, and an output cap.

Prerequisite: a new --no-shell flag / MLR_NO_SHELL env var (one-way
gate) disables the DSL system/exec functions, piped redirects, and
--prepipe/--prepipex; the MCP server sets it on the commands it runs
unless started with --allow-shell.

Adds the github.com/modelcontextprotocol/go-sdk dependency.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Force LF checkout for the embedded SKILL.md (Windows CI fix)

go:embed embeds checkout bytes, so a CRLF checkout on Windows made the
embedded playbook differ per platform and failed
TestPlaybookHasFrontmatter. Pin the file to eol=lf in .gitattributes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Move no-shell test DSL into per-case mlr files (Windows CI fix)

Inline single-quoted DSL in cmd files is mangled by the Windows shell
(single quotes are not quote characters there); the harness's
put -f ${CASEDIR}/mlr pattern avoids shell quoting entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 19:41:49 -04:00
John Kerl
91eaff1341
Lint round 5+6: staticcheck and errcheck to zero (#2130)
* refine the plan

* Fix all staticcheck lint findings (uncapped)

golangci-lint's default max-same-issues=3 was hiding most of the backlog:
the true pre-fix count was 69 staticcheck findings, not 34. This fixes all
of them, driving staticcheck to zero:

- ST1023/QF1011 (37): omit explicit types inferred from the RHS
- S1009/S1031 (15): drop redundant nil checks before len()/range
- SA9003 (9): remove comment-only empty branches, keeping the comments
- QF1007 (3): merge conditional assignment into declaration
- QF1006 (3): lift break conditions into loop conditions
- QF1001 (3): apply De Morgan's law / name the negated predicate

Also updates plans/lintfixes.md with the cap discovery and the corrected
errcheck picture (1202 uncapped, ~949 of them fmt.Fprint*).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Drive errcheck to zero: config for bulk categories, propagate real errors

Adds .golangci.yml with errcheck exclude-functions for fmt.Fprint* (usage
printers), (*bufio.Writer).Write/WriteString (sticky errors, surfaced at the
now-checked final Flush), and (*strings.Builder).WriteString; pins
max-issues-per-linter/max-same-issues to 0 so CI reports true counts.

Real error paths now propagate instead of being dropped:
- Finalize{Reader,Writer}Options in join/put/filter/split/tee and the
  repl/script entry points: 'mlr join -i badformat' now errors instead of
  silently using wrong separators
- final output-stream Flush in pkg/stream: write failure no longer exits 0
- DSL emit/print/dump redirect writes, matching their sibling branches
- CSV writer WriteCSVRecordMaybeColorized, close-time Flush in file output
  handlers, ENV[...] Setenv, REPL record-write and redirect-close errors
- termcvt write-side Close before rename (had "TODO: check return status")

The rest are deliberate ignores, marked with _ = and a comment where the
reason isn't obvious: unset-of-missing-path no-ops, read-side closes,
mid-stream FlushOnEveryRecord, init-time strftime registrations, in-memory
usage-capture pipes, and regtest-harness env/temp-file teardown.

golangci-lint now reports 0 issues on ./cmd/mlr ./pkg/... with all caps off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 11:42:08 -04:00
John Kerl
2bec10fc54
Fix govet lint findings; fix masked unset-on-array error path (#2129)
* plans/lintfixes.md

* plans/lintfixes.md

* Fix remaining govet lint findings

- Rename MarshalJSON -> FormatAsJSON on Mlrval and Mlrmap (govet
  stdmethods): the methods shadowed json.Marshaler with an
  incompatible signature.
- Remove unreachable return after exhaustive if-else in
  pkg/mlrval/mlrval_collections.go (govet unreachable).
- Update plans/lintfixes.md with current status: 84 findings remain
  (50 errcheck, 34 staticcheck).

Part of #2109.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Return nil on successful single-index array unset

removeIndexedOnArray removed the element on the in-bounds path but
then fell through to return an "array index out of bounds for unset"
error, so the success path never returned nil. Callers currently
ignore the error, which masked this; return nil on success so that
upcoming errcheck fixes can propagate the error meaningfully. This
matches removeIndexedOnMap, which returns nil on success.

Add unit tests for RemoveIndexed on arrays.

Part of #2109.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 10:31:08 -04:00
John Kerl
d00de0f71a
Batch-allocate per-record objects; reuse CSV writer field buffer (#2083)
After batch-arena field allocation, profiling cat over 1M-record CSV showed
the remaining ~5M allocations were almost entirely per-record (one each):
the Mlrmap struct, the RecordAndContext wrapper, the CSV writer's []string,
and the go-csv parser's own buffers.

Address the first three:

- mlrval.RecordArena gains NewRecord(), vending the Mlrmap struct itself from
  a per-batch slab (respecting --no-hash-records). Rolled out to every
  line-based reader (CSV, CSV-lite, TSV, DKVP, NIDX, PPRINT, XTAB, DKVPX) in
  place of NewMlrmapAsRecord.

- The CSV reader batch-allocates RecordAndContext wrappers from a per-batch
  slab instead of one heap object per record (comment/output-string entries
  still allocate individually, but they are rare).

- RecordWriterCSV reuses a single fieldsBuffer []string across records instead
  of allocating one per Write; WriteCSVRecordMaybeColorized consumes it
  synchronously and the writer is single-goroutine, so this is safe.

Effect (big.*, 1M records, cat, best of 5):
  csv   0.26 -> 0.22
  dkvp  0.51 -> 0.45  (Mlrmap slab)

For CSV, cat's allocation-object count drops ~5.0M -> ~2.1M. The remaining
~2M are the go-csv parser's per-record backing string and field slice, which
are intrinsic to parsing and would require a zero-copy/batch-slab parser
rework. A CPU profile of cat now shows it is I/O-bound (syscall ~56%, bufio
read+flush), with allocation/GC down to ~10% -- i.e. further allocation
trimming no longer moves cat's wall-clock. GOGC=off confirms (no change).

Verified: go test ./pkg/... and full regression suite pass; output is
byte-identical across all formats including record-retaining verbs (tac),
hashed and --no-hash-records.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 17:03:04 -04:00
John Kerl
a723f6cdaa
Fix PPRINT alignment with multi-character OFS (#1819) (#2063)
* Fix column alignment for wide and combining Unicode chars (#1520, #379)

PPRINT, markdown-aligned, and XTAB writers measured column widths with
utf8.RuneCountInString, which counts codepoints rather than terminal
display columns. East-Asian fullwidth characters (counted as 1 but
displayed as 2) and zero-width combining marks (counted as 1 but
displayed as 0) both caused misalignment.

Add lib.DisplayWidth wrapping uniseg.StringWidth, and use it from the
three writers. The XTAB right-aligned path also drops fmt.Sprintf with
%*s since Go's %*s pads by rune count too.

UTF8Strlen is unchanged so DSL strlen semantics are preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix PPRINT alignment with multi-character OFS (#1819)

Previously, writePadding repeated the OFS string for each padding slot, so
non-space or multi-character OFS values produced misaligned columns and
leaked the separator into padding (e.g. --ofs=XY yielded aXYXYXYXYb).

Pad with spaces instead, leaving OFS to act only as the column separator.
The default single-space OFS is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Use non-space multi-char OFS in test 0021 for Windows portability

PowerShell collapses single-quoted '  ' in command lines, which broke
the test on the windows-latest CI runner. Switch to --ofs XY, which
exercises the same multi-character padding code path without shell
quoting hazards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 12:45:49 -04:00
John Kerl
16209eecf8
Fix column alignment for wide and combining Unicode chars (#1520, #379) (#2061)
PPRINT, markdown-aligned, and XTAB writers measured column widths with
utf8.RuneCountInString, which counts codepoints rather than terminal
display columns. East-Asian fullwidth characters (counted as 1 but
displayed as 2) and zero-width combining marks (counted as 1 but
displayed as 0) both caused misalignment.

Add lib.DisplayWidth wrapping uniseg.StringWidth, and use it from the
three writers. The XTAB right-aligned path also drops fmt.Sprintf with
%*s since Go's %*s pads by rune count too.

UTF8Strlen is unchanged so DSL strlen semantics are preserved.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 12:12:54 -04:00
John Kerl
e0cf596853
Add --omd-aligned flag for column-padded markdown output (#2057)
Adds a new Markdown-only flag --omd-aligned (alias --omarkdown-aligned)
that left-justifies cells and pads each column to a uniform width.
The rendered table is unaffected; the goal is readability of the raw
markdown source. The flag implies --omd, so users do not need to pass
both.

Implementation batches records by schema (like pprint), computes max
column widths, then emits header / separator / data rows with bars
vertically aligned. Default markdown writer behavior is unchanged.
2026-05-16 11:41:01 -04:00
chaoliang yan
5e8175bc6b
fix: preserve key order in YAML output (#2034)
* fix: preserve key order in YAML output

MlrmapToYAMLNative converted the ordered Mlrmap to map[string]interface{},
which loses insertion order. yaml.v3.Marshal then sorts Go map keys
alphabetically, so YAML output always had sorted keys despite other formats
(JSON, pretty-print) correctly preserving order.

Switch to building yaml.Node trees that preserve the Mlrmap's linked-list
iteration order. Use yaml.Node.Encode for scalar values to correctly handle
edge cases (NaN/Inf, non-UTF-8 strings).

Fixes #2028.

* Update pkg/mlrval/mlrval_yaml_test.go with copilot code-review feedback

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: lawrence3699 <lawrence3699@users.noreply.github.com>
Co-authored-by: John Kerl <kerl.john.r@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-12 09:43:09 -04:00
John Kerl
761b46219c
Some fixes for staticcheck (#2006)
* Implement some staticcheck fixes

* make fmt

* more

* more staticcheck
2026-03-03 09:27:03 -05:00
John Kerl
5f14dbc752
Add LRU-caching for split (#2003) 2026-03-02 23:31:44 -05:00
John Kerl
102c624cee
Add DKVPX file format (#2002)
* Initial DKVPX parser

* pkg/input/record_reader_dkvpx*go

* pkg/output/record_writer_dkvpx.go

* docs update

* make fmt

* perf note
2026-03-02 22:35:08 -05:00
Balki
d9ee24460c
Add unicode printing support for barred output (#1984) 2026-02-21 23:15:03 -05:00
Stephen Kitt
f0a7c5832f
Performance and style fixes (#1981)
* Switch to integer ranges in for loops

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Switch to slices functions where appropriate

A number of utility functions can be replaced outright; since Miller
can technically be used as a library, these are deprecated rather than
removed. go:fix directives ensure that they can be replaced
automatically.

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Switch to reflect.TypeFor

This is slightly more efficient than TypeOf when the type is known at
compile time.

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Switch to strings.SplitSeq instead of strings.Split

SplitSeq results in fewer allocations.

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Drop obsolete build directives

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Use min/max instead of explicit comparisons

Signed-off-by: Stephen Kitt <steve@sk2.org>

* Append slices instead of looping

Signed-off-by: Stephen Kitt <steve@sk2.org>

---------

Signed-off-by: Stephen Kitt <steve@sk2.org>
2026-02-18 09:19:31 -05:00
John Kerl
5eb40c9e7b
Multiple style updates (#1974)
* Comment style

* IRecordTransformer -> RecordTransfomer

* make fmt

* else-return style mod

* snake-case -> camel-case

* Remove redundant err = nil and similar zero-value initializations.

* redundant break;

* bugfix

* neaten

* typofix

* simplify/standardize init of zero-length slices

* Standardize fmt.Fprintf w/ errors

* fix double print of "mlr:"

* neatening

* Uniformize error messages

* make docs

* avoid shadowing package names

* shorten some receiver names
2026-02-16 15:49:21 -05:00
John Kerl
a3b5d25933
Neaten some comments (#1973)
* comment-neaten

* comment-neaten

* more
2026-02-16 13:38:49 -05:00
John Kerl
daae7ff7f0
Add DCF (Debian Control Format) as supported file type (#1970)
* reader

* record writer

* pkg/output/record_writer_dcf.go

* test/input/test.dcf

* test/cases/io-dcf/

* pkg/cli/option_parse.go

* make fmt

* make dev

* docs and `make dev`
2026-02-16 11:10:39 -05:00
John Kerl
f375440bd4
Support YAML I/O (#1963)
* docs/src/proposal-yaml-io.md

* initial step

* Add `--y2c` etc

* test cases

* docs

* more testing

* more

* more test cases

* git rm docs/src/proposal-yaml-io.md

* make dev

* ylistwrap -> yarray
2026-02-15 09:24:38 -05:00
John Kerl
7a1943acdb
Resolve a few to-do comments in the source code (#1951)
* Resolve a few to-do comments in the source code

* more

* more
2026-02-01 18:14:20 -05:00
John Kerl
7da6f1453d
Replace list.List with Go slices (#1950)
* Add .vscode to .gitignore

* Replace `list.List` with slices
2026-02-01 17:22:28 -05:00
John Kerl
3b9f169162
Support -o jsonl as well as --ojsonl (#1879)
* `mlr sort -b` feature

* mlr regtest -p test/cases/cli-help && make dev

* Support `-o jsonl` as well as `--ojsonl`
2025-09-02 16:47:19 -04:00
John Kerl
ea242a242a
Docs for new surv verb (#1807) 2025-05-15 19:41:58 -04:00
John Kerl
34bc8a1c3d
Fix print within begin{}/end{} (#1795)
* codemod per se

* unit-test coverage

* lint
2025-05-01 17:18:17 -04:00
John Kerl
5424e753a4
Static-check fixes from @lespea #1657, batch 8/n (#1710) 2024-10-27 12:16:49 -04:00
John Kerl
04a9b9decd
Static-check fixes from @lespea #1657, batch 3/n (#1705)
* Static-check fixes from @lespea #1657, batch 2/n

* Static-check fixes from @lespea #1657, batch 3/n
2024-10-27 11:55:38 -04:00
John Kerl
047cb4bc28
Static-check fixes from @lespea #1657, batch 1/n (#1703) 2024-10-27 11:42:43 -04:00
Adam Lesperance
085e831668
The package version must match the major tag version (#1654)
* Update package version

* Update makefile targets

* Update readme packages

* Remaining old packages via rg/sd
2024-09-20 12:10:11 -04:00
John Kerl
e714738a7d
Fix typo in online help for --no-jlistwrap (#1541)
* Add --no-auto-unsparsify flag

* Fix typo in online help for `--no-jlistwrap`

* Artifacts from `make dev`
2024-04-11 08:12:45 -04:00
John Kerl
f01bb92da7
Avoid spurious [] on JSON output in some cases (#1528)
* JSON empty vs `[]` handling [WIP]

* unit-test mods
2024-03-16 17:00:59 -04:00
John Kerl
ac65675ab1
Auto-unsparsify CSV and TSV on output (#1479)
* Auto-unsparsify CSV

* Update unit-test cases

* More unit-test cases

* Key-change handling for CSV output

* Same for TSV, with unit-test and doc updates
2024-01-20 18:43:49 -05:00
John Kerl
af021f28d7
Support markdown format on input (#1478)
* Support markdown on input

* unit-test files

* doc mods

* Unit-test cases for I/O-format keystroke-savers

* -i/-o md as well as -i/-o markdown
2024-01-20 16:51:15 -05:00
John Kerl
bfc829a381
Internal name-neatens (#1475) 2024-01-20 13:36:28 -05:00
John Kerl
268a96d002
Export library code in pkg/ (#1391)
* Export library code in `pkg/`

* new doc page
2023-09-10 17:15:13 -04:00