mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
add CLAUDE.md and .claude/docs for agent context
This commit is contained in:
parent
b1734bbc34
commit
1fbf855884
50
.claude/docs/cli.md
Normal file
50
.claude/docs/cli.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# CLI
|
||||
|
||||
## Entry Point
|
||||
|
||||
`cmd/peco/peco.go` — parses flags, creates `peco.New()`, calls `Run(ctx)`
|
||||
|
||||
## Flags (CLIOptions)
|
||||
|
||||
| Flag | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `--help` | bool | Show help |
|
||||
| `--version` | bool | Show version |
|
||||
| `--query` | string | Initial query string |
|
||||
| `--rcfile` | string | Config file path |
|
||||
| `--buffer-size` | int | Max lines to read (0=unlimited) |
|
||||
| `--null` | bool | Use NUL as line separator |
|
||||
| `--initial-index` | int | Initial cursor position |
|
||||
| `--initial-filter` | string | Initial filter name |
|
||||
| `--prompt` | string | Prompt string |
|
||||
| `--layout` | string | Layout type: top-down, bottom-up |
|
||||
| `--select-1` | bool | Auto-select if single match |
|
||||
| `--exit-zero` | bool | Exit 0 even on cancel |
|
||||
| `--select-all` | bool | Select all lines initially |
|
||||
| `--on-cancel` | string | Cancel behavior: success/error |
|
||||
| `--selection-prefix` | string | Prefix for selected lines |
|
||||
| `--exec` | string | Command to execute with selection |
|
||||
| `--print-query` | bool | Print query as first output line |
|
||||
| `--color` | ColorMode | Color mode: auto, none |
|
||||
| `--height` | string | Terminal height spec |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- 0 — success (lines selected)
|
||||
- 0 — cancel with `--on-cancel success` or `--exit-zero`
|
||||
- 1 — cancel (default)
|
||||
- Custom — from `--exec` command exit status
|
||||
|
||||
## Input
|
||||
|
||||
- Reads from stdin by default
|
||||
- Positional arg → read from file
|
||||
- Supports streaming (infinite) input
|
||||
|
||||
## Output
|
||||
|
||||
- Selected lines to stdout, one per line
|
||||
- With `--print-query`: query string as first line
|
||||
- With `--exec`: pipes selection to command
|
||||
57
.claude/docs/dependencies.md
Normal file
57
.claude/docs/dependencies.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# Internal Dependency Graph
|
||||
|
||||
```
|
||||
cmd/peco → peco (root), internal/util
|
||||
cmd/filterbench → filter
|
||||
|
||||
peco (root) → config, filter, hub, line, pipeline, query, selection, sig
|
||||
→ internal/ansi, internal/keyseq, internal/util, internal/buffer
|
||||
|
||||
config → internal/util
|
||||
|
||||
filter → line, pipeline, internal/util
|
||||
|
||||
selection → line
|
||||
|
||||
line → internal/ansi
|
||||
|
||||
pipeline → line
|
||||
|
||||
internal/buffer → line
|
||||
```
|
||||
|
||||
## Layer Grouping
|
||||
|
||||
### Leaf (no internal deps)
|
||||
- `hub` — message bus, no imports
|
||||
- `query` — query text/caret, no imports
|
||||
- `sig` — signal handling, no imports
|
||||
- `internal/ansi` — ANSI parser, no imports
|
||||
- `internal/keyseq` — key matching, no imports
|
||||
- `internal/util` — platform utils, no imports
|
||||
|
||||
### Core
|
||||
- `line` → internal/ansi
|
||||
- `pipeline` → line
|
||||
- `internal/buffer` → line
|
||||
|
||||
### Processing
|
||||
- `config` → internal/util
|
||||
- `filter` → line, pipeline, internal/util
|
||||
- `selection` → line
|
||||
|
||||
### Application
|
||||
- `peco` (root) → all above
|
||||
- `cmd/peco` → peco, internal/util
|
||||
- `cmd/filterbench` → filter
|
||||
|
||||
## External Dependencies
|
||||
- `github.com/gdamore/tcell/v2` — terminal UI (screen.go)
|
||||
- `github.com/goccy/go-yaml` — config parsing
|
||||
- `github.com/google/btree` — ordered selection storage
|
||||
- `github.com/jessevdk/go-flags` — CLI flag parsing
|
||||
- `github.com/lestrrat-go/pdebug` — debug logging
|
||||
- `github.com/mattn/go-runewidth` — Unicode width calculation
|
||||
- `github.com/stretchr/testify` — test assertions
|
||||
102
.claude/docs/internals.md
Normal file
102
.claude/docs/internals.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# Internals
|
||||
|
||||
## Concurrency Model
|
||||
|
||||
Three main goroutines coordinated via context cancellation:
|
||||
|
||||
1. **Input loop** (`input.go`) — reads terminal events, resolves key sequences via Keymap, dispatches actions
|
||||
2. **View loop** (`view.go`) — renders screen in response to hub messages (draw, paging, status)
|
||||
3. **Filter loop** (`filter.go`) — executes query against line buffer when query changes
|
||||
|
||||
Communication → **Hub** (`hub/`), central message bus with typed generic channels.
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
stdin/file → Source → MemoryBuffer
|
||||
↓
|
||||
User keystroke → Input → Hub.SendQuery() → Filter loop
|
||||
↓
|
||||
Filter.Apply() → FilteredBuffer → Hub.SendDraw() → View loop
|
||||
↓
|
||||
View → Layout → Screen (tcell) → terminal
|
||||
```
|
||||
|
||||
## Hub Message Types
|
||||
|
||||
| Channel | Payload | Sender | Receiver |
|
||||
|---------|---------|--------|----------|
|
||||
| `QueryCh` | `string` | Input (action) | Filter loop |
|
||||
| `DrawCh` | `*DrawOptions` | Filter, actions | View loop |
|
||||
| `PagingCh` | `PagingRequest` | Input (action) | View loop |
|
||||
| `StatusMsgCh` | `StatusMsg` | Various | View loop |
|
||||
|
||||
Hub supports **batch mode** — multiple sends within `Batch()` callback are processed together.
|
||||
|
||||
## Buffer Architecture
|
||||
|
||||
- `MemoryBuffer` — stores all input lines, grows as Source reads
|
||||
- `FilteredBuffer` — wraps any Buffer with index range (page slice)
|
||||
- `Source` — implements `pipeline.Source`, reads input lines into MemoryBuffer
|
||||
- `ContextBuffer` — adds surrounding context lines for zoom view
|
||||
- `CurrentLineBuffer()` returns raw MemoryBuffer (no filter) or FilteredBuffer (after filter)
|
||||
|
||||
## Filter Pipeline
|
||||
|
||||
1. Query change arrives via Hub
|
||||
2. Filter loop creates pipeline: `MemoryBufferSource → filter.Apply → MemoryBuffer`
|
||||
3. Filter.Apply runs in parallel chunks (if `SupportsParallel()`)
|
||||
4. Results collected into new MemoryBuffer → set as CurrentLineBuffer
|
||||
5. Hub.SendDraw() triggers View redraw
|
||||
|
||||
## Screen Abstraction
|
||||
|
||||
- `Screen` interface wraps terminal operations
|
||||
- `TcellScreen` — production impl using tcell/v2
|
||||
- `InlineScreen` — wraps TcellScreen for height-limited display
|
||||
- `DummyScreen` — test mock with event injection
|
||||
|
||||
## Layout System
|
||||
|
||||
- `BasicLayout` composes: `UserPrompt` + `ListArea` + `StatusBar`
|
||||
- Layout variants registered via `RegisterLayout(name, LayoutBuilder)`
|
||||
- Built-in: `top-down` (default), `bottom-up`, `top-down-query-bottom`
|
||||
- `AnchorSettings` controls vertical positioning (top/bottom anchor)
|
||||
|
||||
## Key Sequence Resolution
|
||||
|
||||
- `internal/keyseq.Keyseq` uses AhoCorasick matcher
|
||||
- Supports multi-key sequences (e.g., C-x,C-c)
|
||||
- Longest-match-wins semantics
|
||||
- `InMiddleOfChain()` indicates partial match in progress
|
||||
|
||||
## Selection Model
|
||||
|
||||
- `selection.Set` uses `google/btree` for ordered storage by line ID
|
||||
- Supports: single select, multi-select (toggle), range select, select-all
|
||||
- Sticky selection — persists across query changes (configurable)
|
||||
|
||||
## Action System
|
||||
|
||||
- ~40 built-in actions in `action.go`
|
||||
- Actions implement `Action` interface: `Execute(ctx, *Peco, Event)`
|
||||
- `ActionFunc` — function adapter with `Register()` for key binding
|
||||
- Combined actions — multiple actions bound to single key sequence
|
||||
- `Keymap.LookupAction(Event) → Action` — resolve event to action
|
||||
|
||||
## State Objects
|
||||
|
||||
| State | Purpose |
|
||||
|-------|---------|
|
||||
| `Location` | Current page, line number, offset, per-page count |
|
||||
| `SingleKeyJumpState` | Single-key jump mode toggle and prefix map |
|
||||
| `ZoomState` | Zoom view buffer and line reference |
|
||||
| `FrozenState` | Frozen source buffer for suspend/resume |
|
||||
| `QueryExecState` | Query execution delay timer |
|
||||
|
||||
## Object Pools
|
||||
|
||||
- `line.GetMatched/ReleaseMatched` — pool for Matched line wrappers
|
||||
- `internal/buffer.GetLineListBuf/ReleaseLineListBuf` — pool for line slices
|
||||
189
.claude/docs/packages.md
Normal file
189
.claude/docs/packages.md
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# Package Map
|
||||
|
||||
## peco (root)
|
||||
|
||||
Interactive filtering tool core. Holds global state, goroutine loops, UI components.
|
||||
|
||||
- **New() → *Peco** — create new instance
|
||||
- **(*Peco).Setup() → error** — initialize from config/options
|
||||
- **(*Peco).Run(ctx) → error** — main event loop
|
||||
- **(*Peco).ApplyConfig(CLIOptions) → error** — apply CLI flags to config
|
||||
- **(*Peco).SetupSource(ctx) → (*Source, error)** — initialize input source
|
||||
- **(*Peco).PrintResults()** — output selected lines to stdout
|
||||
- **(*Peco).CurrentLineBuffer() → Buffer** — active line buffer (raw or filtered)
|
||||
- **(*Peco).ExecQuery(ctx, func()) → bool** — execute filter with debounce
|
||||
- Key types: `Peco`, `Buffer`, `FilteredBuffer`, `MemoryBuffer`, `Source`, `Screen`, `Layout`, `Action`, `Keymap`, `Event`, `CLIOptions`, `Location`, `PageCrop`
|
||||
- Key interfaces: `MessageHub`, `Screen`, `Layout`, `Action`, `ActionMap`, `Buffer`, `Keyseq`, `ConfigReader`
|
||||
- Screen impls: `TcellScreen` (production), `InlineScreen` (height-limited), `DummyScreen` (tests)
|
||||
- Layout impls: `BasicLayout` with builders: `DefaultLayout`, `BottomUpLayout`, `TopDownQueryBottomLayout`
|
||||
- Files: `peco.go`, `action.go`, `buffer.go`, `caret.go`, `event.go`, `filter.go`, `input.go`, `keymap.go`, `layout.go`, `layout_any.go`, `layout_windows.go`, `options.go`, `page.go`, `screen.go`, `screen_inline.go`, `source.go`, `state.go`, `view.go`, `vertical_anchor_gen.go`
|
||||
- Imports: config, filter, hub, line, pipeline, query, selection, sig, internal/ansi, internal/keyseq, internal/util
|
||||
|
||||
## cmd/peco
|
||||
|
||||
CLI entry point.
|
||||
|
||||
- Parses flags via `go-flags`, creates `peco.New()`, calls `Run(ctx)`
|
||||
- Files: `peco.go`
|
||||
- Imports: peco (root), internal/util
|
||||
|
||||
## cmd/filterbench
|
||||
|
||||
Benchmark tool for filter performance.
|
||||
|
||||
- Files: `main.go`
|
||||
- Imports: filter
|
||||
|
||||
## config/
|
||||
|
||||
Configuration loading and types.
|
||||
|
||||
- **Config** — main config struct (Keymap, Action, Style, Layout, CustomFilter, SingleKeyJump, Height, etc.)
|
||||
- **(*Config).Init() → error** — set defaults
|
||||
- **(*Config).ReadFilename(string) → error** — load YAML config file
|
||||
- **LocateRcfile(Locator) → (string, error)** — find config file path
|
||||
- Key types: `Config`, `StyleSet`, `Style`, `Attribute`, `OnCancelBehavior`, `ColorMode`, `CustomFilterConfig`, `SingleKeyJumpConfig`, `HeightSpec`
|
||||
- Color constants: `ColorDefault`, `ColorBlack`..`ColorWhite`, `AttrBold`, `AttrUnderline`, `AttrReverse`, `AttrTrueColor`
|
||||
- Layout constants: `LayoutTypeTopDown`, `LayoutTypeBottomUp`, `LayoutTypeTopDownQueryBottom`
|
||||
- Files: `config.go`, `style.go`, `height.go`, `layout.go`
|
||||
- Imports: internal/util
|
||||
|
||||
## filter/
|
||||
|
||||
Filter algorithm implementations.
|
||||
|
||||
- **Filter** interface — `Apply(ctx, []line.Line, ChanOutput) → error`, `BufSize() → int`, `NewContext(ctx, string) → ctx`, `String() → string`, `SupportsParallel() → bool`
|
||||
- **Collector** interface (optional) — `ApplyCollect(ctx, []line.Line) → ([]line.Line, error)`
|
||||
- **Set** — filter collection with rotation: `Add(Filter)`, `Rotate()`, `Current() → Filter`, `SetCurrentByName(string) → error`
|
||||
- Implementations: `NewIgnoreCase()`, `NewCaseSensitive()`, `NewSmartCase()`, `NewRegexp()`, `NewIRegexp()`, `NewFuzzy(longestSort bool)`, `NewExternalCmd(name, cmd string, args []string, threshold int, idgen IDGenerator, enableSep bool)`
|
||||
- Files: `filter.go`, `base.go`, `regexp.go`, `fuzzy.go`, `external.go`, `set.go`
|
||||
- Imports: line, pipeline, internal/util
|
||||
|
||||
## hub/
|
||||
|
||||
Central message bus for goroutine communication.
|
||||
|
||||
- **New(bufsize int) → *Hub** — create hub with channel buffer size
|
||||
- **(*Hub).SendDraw(ctx, *DrawOptions)** — trigger screen redraw
|
||||
- **(*Hub).SendQuery(ctx, string)** — send query change
|
||||
- **(*Hub).SendPaging(ctx, PagingRequest)** — send paging command
|
||||
- **(*Hub).SendStatusMsg(ctx, string, time.Duration)** — show status message
|
||||
- **(*Hub).Batch(ctx, func(ctx))** — batch multiple sends atomically
|
||||
- Channel accessors: `DrawCh()`, `PagingCh()`, `QueryCh()`, `StatusMsgCh()`
|
||||
- Key types: `Hub`, `Payload[T]`, `DrawOptions`, `PagingRequest`, `PagingRequestType`, `StatusMsg`
|
||||
- Paging types: `ToLineAbove`, `ToLineBelow`, `ToScrollPageDown`, `ToScrollPageUp`, `ToScrollLeft`, `ToScrollRight`, `ToScrollFirstItem`, `ToScrollLastItem`, `ToLineInPage`
|
||||
- Files: `hub.go`, `draw.go`, `paging.go`, `paging_request_type_gen.go`
|
||||
- Imports: (none internal)
|
||||
|
||||
## line/
|
||||
|
||||
Line data types for display and selection.
|
||||
|
||||
- **Line** interface — `ID() → uint64`, `Buffer() → string`, `DisplayString() → string`, `Output() → string`, `IsDirty() → bool`, `SetDirty(bool)`, implements `btree.Item`
|
||||
- **NewRaw(id uint64, s string, enableSep bool, stripANSI bool) → *Raw** — create raw line
|
||||
- **NewMatched(Line, [][]int) → *Matched** — wrap line with match indices
|
||||
- **GetMatched(Line, [][]int) → *Matched** — pooled allocation
|
||||
- **ReleaseMatched(*Matched)** — return to pool
|
||||
- **IDGenerator** interface — `Next() → uint64`
|
||||
- Files: `raw.go`, `matched.go`
|
||||
- Imports: internal/ansi, btree
|
||||
|
||||
## pipeline/
|
||||
|
||||
Generic source→acceptor→destination pipeline.
|
||||
|
||||
- **New() → *Pipeline** — create pipeline
|
||||
- **(*Pipeline).SetSource(Source)** — set data source
|
||||
- **(*Pipeline).Add(Acceptor)** — add processing stage
|
||||
- **(*Pipeline).SetDestination(Destination)** — set terminal stage
|
||||
- **(*Pipeline).Run(ctx) → error** — execute pipeline
|
||||
- Key interfaces: `Source` (`Start`, `Reset`), `Acceptor` (`Accept`), `Destination` (`Accept`, `Reset`, `Done`), `Suspender` (optional `Suspend`/`Resume`)
|
||||
- **ChanOutput** (chan line.Line) — `Send(ctx, line.Line) → error`, `OutCh() → <-chan line.Line`
|
||||
- **NewQueryContext(ctx, string) → ctx** / **QueryFromContext(ctx) → string** — pass query through context
|
||||
- Files: `pipeline.go`
|
||||
- Imports: line
|
||||
|
||||
## query/
|
||||
|
||||
Query text and caret management.
|
||||
|
||||
- **Text** — query string with save/restore: `Set(string)`, `Reset()`, `SaveQuery()`, `RestoreSavedQuery()`, `DeleteRange(int, int)`, `InsertAt(rune, int)`, `String()`, `Len()`, `RuneSlice()`, `RuneAt(int)`
|
||||
- **Caret** — cursor position: `Pos() → int`, `SetPos(int)`, `Move(int)`
|
||||
- Files: `query.go`
|
||||
- Imports: (none internal)
|
||||
|
||||
## selection/
|
||||
|
||||
Ordered selection storage using btree.
|
||||
|
||||
- **New() → *Set** — create selection set
|
||||
- **(*Set).Add(line.Line)** — add to selection
|
||||
- **(*Set).Remove(line.Line)** — remove from selection
|
||||
- **(*Set).Has(line.Line) → bool** — check membership
|
||||
- **(*Set).Len() → int** — count selected
|
||||
- **(*Set).Ascend(func(line.Line) bool)** — iterate in order
|
||||
- **(*Set).Copy(dst *Set)** — copy all items
|
||||
- **RangeStart** — range selection start marker: `Valid()`, `Value()`, `SetValue(int)`, `Reset()`
|
||||
- Files: `selection.go`
|
||||
- Imports: line, btree
|
||||
|
||||
## sig/
|
||||
|
||||
OS signal handling.
|
||||
|
||||
- **New(handler ReceivedHandler, sigs ...os.Signal) → *Handler**
|
||||
- **(*Handler).Loop(ctx, func()) → error** — signal listening loop
|
||||
- **ReceivedHandler** interface — `Handle(os.Signal)`
|
||||
- Files: `sig.go`
|
||||
- Imports: (none internal)
|
||||
|
||||
## internal/ansi
|
||||
|
||||
ANSI escape sequence parser.
|
||||
|
||||
- **Parse(string) → ParseResult** — strip ANSI, extract color spans
|
||||
- **ExtractSegment([]AttrSpan, start, end int) → []AttrSpan** — slice attr spans for substring
|
||||
- Key types: `ParseResult` (`Stripped string`, `Attrs []AttrSpan`), `AttrSpan` (`Fg, Bg Attribute`, `Length int`)
|
||||
- Files: `parser.go`
|
||||
- Imports: (none internal)
|
||||
|
||||
## internal/buffer
|
||||
|
||||
Line list buffer pool.
|
||||
|
||||
- **GetLineListBuf() → []line.Line** — get from pool
|
||||
- **ReleaseLineListBuf([]line.Line)** — return to pool
|
||||
- Files: `line.go`
|
||||
- Imports: line
|
||||
|
||||
## internal/keyseq
|
||||
|
||||
Key sequence matching (multi-key bindings).
|
||||
|
||||
- **New() → *Keyseq** — create matcher (uses AhoCorasick internally)
|
||||
- **(*Keyseq).Add(KeyList, any)** — register key sequence → action
|
||||
- **(*Keyseq).Compile() → error** — build matcher
|
||||
- **(*Keyseq).AcceptKey(Key) → (any, error)** — feed key, get action if matched
|
||||
- **ToKeyList(string) → (KeyList, error)** — parse "C-x,C-c" → KeyList
|
||||
- **KeyEventToString(KeyType, rune, ModifierKey) → (string, error)** — event → name
|
||||
- Key types: `Key`, `KeyList`, `KeyType`, `ModifierKey`
|
||||
- Matcher impls: Trie, TernarySearch, AhoCorasick (AhoCorasick used by default)
|
||||
- Files: `keyseq.go`, `keys.go`, `trie.go`, `ternary.go`, `ahocorasick.go`
|
||||
- Imports: (none internal)
|
||||
|
||||
## internal/util
|
||||
|
||||
Platform utilities.
|
||||
|
||||
- **IsTty(io.Reader) → bool** — check if reader is terminal
|
||||
- **Homedir() → (string, error)** — user home directory
|
||||
- **Shell(ctx, string) → *exec.Cmd** — create shell command
|
||||
- **StripANSISequence(string) → string** — remove ANSI escapes (deprecated, use internal/ansi)
|
||||
- **IsCollectResultsError(error) → bool** — check for collect-results sentinel
|
||||
- **IsIgnorableError(error) → bool** — check for ignorable errors
|
||||
- **GetExitStatus(error) → (int, bool)** — extract exit code
|
||||
- Platform files: `tty_posix.go`, `tty_bsd.go`, `tty_windows.go`, `shell_unix.go`, `shell_windows.go`, `homedir_posix.go`, `homedir_darwin.go`, `homedir_windows.go`
|
||||
- Files: `util.go`
|
||||
- Imports: (none internal)
|
||||
58
.claude/docs/testing.md
Normal file
58
.claude/docs/testing.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# Testing
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
make test # go test -v -race ./...
|
||||
go test -v -run TestFoo ./... # single test, all packages
|
||||
go test -v -run TestFoo ./filter/ # single test, specific package
|
||||
go test -race -coverprofile=coverage.out ./... # coverage
|
||||
```
|
||||
|
||||
## Test Package Convention
|
||||
|
||||
- Tests use same package name (not `_test` suffix) — white-box testing
|
||||
- Exception: some packages use `_test` suffix for external testing
|
||||
|
||||
## Key Test Helpers
|
||||
|
||||
- `newPeco() → *Peco` — creates test instance with DummyScreen, default config
|
||||
- `NewDummyScreen() → *DummyScreen` — mock terminal; supports `SendEvent(Event)` for input injection
|
||||
- DummyScreen has fixed size, no-op rendering, collects events
|
||||
|
||||
## Test Patterns
|
||||
|
||||
- Table-driven tests with `t.Run()` subtests
|
||||
- Regression tests for GitHub issues in `issues_test.go`
|
||||
- Filter tests in `filter/filter_test.go`, `filter/base_test.go`
|
||||
- Hub tests in `hub/hub_test.go`
|
||||
- Key sequence tests in `internal/keyseq/trie_test.go`, `ahocorasick_test.go`, `ternary_test.go`
|
||||
- Pipeline tests in `pipeline/pipeline_test.go`
|
||||
- Selection tests in `selection/selection_test.go`
|
||||
- Query tests in `query/query_test.go`
|
||||
|
||||
## Benchmark Tests
|
||||
|
||||
- `filter/bench_test.go` — filter algorithm benchmarks
|
||||
- `hub/bench_test.go` — hub message passing benchmarks
|
||||
- `line/bench_test.go` — line allocation benchmarks
|
||||
- `internal/ansi/bench_test.go` — ANSI parsing benchmarks
|
||||
- `internal/util/bench_test.go` — utility benchmarks
|
||||
- `cmd/filterbench/` — standalone filter benchmark CLI
|
||||
|
||||
## No Test Data Directory
|
||||
|
||||
- No `testdata/` or golden files
|
||||
- Tests use inline data and programmatic setup
|
||||
|
||||
## Build Tags
|
||||
|
||||
- Platform-specific files: `_posix.go`, `_bsd.go`, `_windows.go`, `_darwin.go`
|
||||
- No custom build tags for testing
|
||||
|
||||
## Code Generation
|
||||
|
||||
- `go generate ./...` — runs `stringer` for enum types
|
||||
- Generated files: `vertical_anchor_gen.go`, `hub/paging_request_type_gen.go`
|
||||
104
CLAUDE.md
Normal file
104
CLAUDE.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<!-- Agent-consumed file. Keep terse, unambiguous, machine-parseable. -->
|
||||
|
||||
# CLAUDE.md
|
||||
|
||||
peco is an interactive filtering tool for the terminal, written in Go.
|
||||
|
||||
## Pre-Read Rules
|
||||
|
||||
Read the linked doc BEFORE working in that area. No exceptions.
|
||||
|
||||
| Trigger | Doc |
|
||||
|---------|-----|
|
||||
| Working with any package API or adding imports | `.claude/docs/packages.md` |
|
||||
| Modifying cross-package dependencies | `.claude/docs/dependencies.md` |
|
||||
| Writing or running tests | `.claude/docs/testing.md` |
|
||||
| Modifying CLI flags, entry point, or output | `.claude/docs/cli.md` |
|
||||
| Modifying concurrency, hub, buffers, filters, layout, screen, actions | `.claude/docs/internals.md` |
|
||||
|
||||
## Build & Test Commands
|
||||
|
||||
```bash
|
||||
make # Download deps and build (default target)
|
||||
make build # Build binary to releases/peco_<os>_<arch>/peco
|
||||
make test # Run all tests: go test -v ./...
|
||||
make deps # Download Go module dependencies
|
||||
make clean # Remove build artifacts
|
||||
```
|
||||
|
||||
Run a single test:
|
||||
```bash
|
||||
go test -v -run TestFunctionName ./...
|
||||
go test -v -run TestFunctionName ./filter/ # for a specific package
|
||||
```
|
||||
|
||||
The entry point is `cmd/peco/peco.go`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Concurrency Model
|
||||
|
||||
peco runs three main goroutines coordinated via context cancellation:
|
||||
|
||||
- **Input loop** (`input.go`) — reads termbox key events, resolves key sequences via Keymap, dispatches actions
|
||||
- **View loop** (`view.go`) — renders screen in response to draw/paging/status messages
|
||||
- **Filter loop** (`filter.go`) — executes queries against the line buffer when query text changes
|
||||
|
||||
These goroutines communicate through the **Hub** (`hub/`), a central message bus with typed channels: `QueryCh`, `DrawCh`, `PagingCh`, `StatusMsgCh`.
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Source** (`source.go`) reads input lines (stdin or file), implements `pipeline.Source`
|
||||
2. User keystrokes trigger actions that modify the query
|
||||
3. Query changes are sent to the Filter loop via Hub
|
||||
4. **Filter** applies the active filter algorithm to produce matched lines
|
||||
5. Results flow through the **Pipeline** (`pipeline/`) as `Source → Acceptor → Destination`
|
||||
6. **View** receives draw messages and delegates to **Layout** (`layout.go`) which composes `UserPrompt`, `ListArea`, and `StatusBar`
|
||||
7. **Screen** (`screen.go`) wraps termbox-go for terminal cell rendering
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
- **`Buffer`** — line storage (`LineAt`, `Size`); implemented by `MemoryBuffer`, `FilteredBuffer`, `Source`
|
||||
- **`Filter`** (in `filter/`) — `Apply(ctx, []line.Line, ChanOutput)` for each filter algorithm (IgnoreCase, CaseSensitive, SmartCase, Regexp, IRegexp, Fuzzy, ExternalCmd)
|
||||
- **`Line`** (`line/`) — represents a single line with `ID`, `Buffer`, `DisplayString`, `Output`
|
||||
- **`Screen`** — terminal abstraction (`Init`, `SetCell`, `Flush`, `PollEvent`); `DummyScreen` used in tests
|
||||
- **`Layout`** — screen composition (`DrawScreen`, `DrawPrompt`, `MovePage`)
|
||||
- **`Action`** — user actions bound to keys (`action.go`); ~40 built-in actions, supports combined action sequences
|
||||
|
||||
### Selection
|
||||
|
||||
Uses `google/btree` for ordered selection storage. Supports multi-select, range mode, and sticky selection (persists across query changes).
|
||||
|
||||
### Key Sequence Resolution
|
||||
|
||||
`internal/keyseq/` implements Trie, TernarySearch, and AhoCorasick for matching multi-key sequences to actions (longest-match-wins).
|
||||
|
||||
### Platform-Specific Code
|
||||
|
||||
Files suffixed `_posix.go` / `_windows.go` in `screen.go` and `internal/util/` handle TTY detection, shell integration, and home directory resolution per platform.
|
||||
|
||||
### Code Generation
|
||||
|
||||
Uses `go:generate` with `stringer` for enum string representations.
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
- `newPeco()` helper creates a test instance with `DummyScreen` (mock terminal)
|
||||
- `NewDummyScreen()` supports event injection for simulating user input
|
||||
- Table-driven tests with `t.Run()` subtests are the common pattern
|
||||
- Regression tests for specific GitHub issues in `issues_test.go`
|
||||
|
||||
## Cache Maintenance
|
||||
|
||||
These docs cache repository state. Still read source before modifying code.
|
||||
|
||||
1. When your changes affect a doc below, update it in the same commit.
|
||||
2. If you notice any doc is wrong or stale — even on an unrelated task — fix it immediately.
|
||||
|
||||
| Doc | Update trigger |
|
||||
|-----|----------------|
|
||||
| `packages.md` | Add/remove/rename exported functions, types, or packages |
|
||||
| `dependencies.md` | Add/remove internal package imports |
|
||||
| `testing.md` | Change test infrastructure, helpers, or test commands |
|
||||
| `cli.md` | Add/remove CLI flags, change exit codes or output format |
|
||||
| `internals.md` | Change concurrency model, hub channels, buffer types, layout system, action system |
|
||||
Loading…
Reference in a new issue