chunk

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

This file chunks shell scripts. Build tasks, deploy scripts, and CI helpers accumulate operational knowledge that exists nowhere else — the comment above a function is often the only documentation an operational procedure has.

Bash gets its own walker rather than a table entry (see languages.go) because of what it *excludes*. A script is mostly top-level commands, not declarations, and indexing those would bury the useful chunks. Only functions and documented variables are emitted.

Package chunk splits a markdown file into retrievable units for embedding, via a heading-tree walk that emits /path·/narrow·/full variants with slugified keys. Notable design choices:

  • API returns structured []Chunk instead of a map[string]string, so the index layer stores key/heading/variant/text without re-parsing a composite key.
  • Chunk text is run through Simplify (simplify.go) before it's stored: HTML comments and generated TOC blocks are dropped and links (`[](…)` and Obsidian `[[…]]`) are reduced to their visible text, so embeddings and snippets aren't padded with URLs, anchors, and markers. Ordinary markdown is kept — the model reads it fine.
  • A synthesized `title` chunk is emitted from frontmatter title/tags, so a note that carries its name only in frontmatter (no body H1) stays findable. Frontmatter is otherwise not embedded.

This file extends the chunker to Go source. Where Document walks a markdown heading tree, GoSource walks a Go file's syntax tree and emits one chunk per symbol — package doc, type, func, method, and documented const/var block. The retrieval surface is the *doc comment + signature*, not the function body: bodies are implementation, the way markdown frontmatter is attributes (see Document) — high-signal names and prose stay, the mechanics don't dilute the index.

Extraction is tree-sitter, not go/parser + go/doc. The reason is uniformity, not capability: every other language here goes through tree-sitter, so Go on go/parser meant two parser stacks doing one job, and every shared concern — signature rendering, line numbering, breadcrumbs — needed writing twice. See walker.go for the machinery this shares with the rest.

Error recovery is *not* the reason. go/parser recovers from a malformed declaration about as well as tree-sitter does, and both keep the declarations that parsed. The real cost of the swap is a cgo grammar in place of the standard library, and re-deriving go/doc's doc↔symbol association by hand — the latter is cheap, because a Go doc comment is just the unbroken comment group directly above a declaration.

This file chunks HCL — Terraform, Terragrunt, Packer, and Nomad configuration. Infrastructure is where "why is it configured this way" questions are hardest to answer by grep, because the answer is usually in a comment above a block whose name you would have to already know.

HCL gets its own walker rather than a table entry (see languages.go) because a block is named by its labels rather than by a field: `resource "aws_s3_bucket" "logs"` is one node whose identity is spread across three children. Joining them is what makes a chunk key match how an engineer refers to the resource.

This file chunks the languages whose declarations all have the same shape: a named node, optionally holding members, documented by the comment group directly above it. Java, C#, Rust, C, C++, Ruby, PHP, Scala, and Lua differ in node names and almost nothing else, so each is a table entry rather than a file.

Go, TypeScript, Python, Bash, HCL, Protobuf, and YAML keep hand-written walkers. That is not an oversight — each has a quirk the table cannot express without growing a flag that only one caller sets: Go qualifies methods by a receiver buried under a pointer and type arguments, TypeScript has to recognise CommonJS export assignments and function-valued consts, and Python keeps its documentation *inside* the body rather than above the declaration. A table is the right tool for the regular cases and the wrong one for the irregular, so the line is drawn there deliberately.

This file chunks Protocol Buffers. A `.proto` file is the closest thing a service-oriented codebase has to an API reference, and the comments above a message, an rpc, or an enum are that reference's prose — so they are worth indexing even though a proto declares no behaviour.

Protobuf gets its own walker rather than a table entry (see languages.go) because it names things by a dedicated child node — `message_name`, `service_name`, `rpc_name` — instead of by a `name` field, and because an rpc is worth qualifying by the service that declares it.

This file chunks Python. Python gets a hand-written walker rather than a table entry (see languages.go) for one reason: its documentation lives *inside* the declaration, not above it. A docstring is the first statement of a module, class, or function body, which inverts the association every other language here uses and which the shared docScanner implements.

Both conventions are honoured, with the docstring winning. A `#` comment above a def is common in real code and is worth indexing, but where a docstring exists it is the author's actual documentation — PEP 257 says so, and every Python tool reads it that way.

This file is the one place a file extension is mapped to a language. Both the indexer (which needs the chunker) and search (which needs the language name for `--lang`) read it, so the set of indexed extensions and the set of filterable languages cannot drift apart — a new language becomes searchable by name the moment it becomes indexable.

This file extends the chunker to TypeScript, the same way gosource.go extends it to Go: one chunk per documented symbol — function, class (+ its methods), interface, type alias, enum, and documented top-level const/var. The retrieval surface is the *JSDoc comment + signature*, not the body: function/method bodies are stripped exactly as Go bodies are, so high-signal names, types, and prose stay while the mechanics don't dilute the index.

TypeScript has no stdlib parser, so we lean on tree-sitter (already a viable cgo dependency, since onnxruntime_go forces CGO_ENABLED=1). The .ts grammar and the .tsx grammar differ only in how they resolve the `<...>` ambiguity (type assertions/generics vs JSX), so each is wrapped once and dispatched by extension in the indexer.

This file holds the machinery every tree-sitter chunker shares, so adding a language is a walk plus an emit rather than a parser.

The split is deliberate: everything here is language-independent — loading a grammar once, associating a comment group with the declaration below it, assembling a chunk's breadcrumb-led text, and keeping keys unique. What stays in each language's file is the part that genuinely differs: which node kinds are declarations, where a name lives, and what counts as a signature.

This is not a configuration DSL. A per-language spec table was the other option and it collapses the moment a language has a quirk — Python keeps its doc *inside* the body, HCL names a block with labels rather than a field, Bash has no doc convention at all. A shared toolkit absorbs those; a schema grows a flag for each one.

This file chunks YAML. YAML needed a granularity decision the other languages did not, because it has no declarations — only nesting — and the obvious readings are both wrong.

Chunking every key floods the index: a Kubernetes manifest has hundreds, and `spec.template.spec.containers.0.image` retrieves nothing a person would search for. Chunking whole files is the opposite failure — a multi-document manifest becomes one blob whose embedding averages a Deployment, a Service, and a ConfigMap into nothing in particular.

So the unit is the **document**, plus its **top-level keys**. A document is what a YAML file actually declares (one object, one release, one workflow), and top-level keys are the sections a person names out loud — `metadata`, `spec`, `data`, `jobs`. Both are bounded, and both match how the files are discussed.

A document that declares `kind` and `metadata.name` is identified by them, because "the api-gateway Deployment" is what someone searches for, not "the third document in deployment.yaml".

Index

Constants

View Source
const (
	VariantTitle  = "title"
	VariantPath   = "path"
	VariantNarrow = "narrow"
	VariantFull   = "full"
	VariantBody   = "body"
)

Variant constants for the chunk kinds this package emits.

View Source
const (
	VariantPackage = "package"
	VariantType    = "type"
	VariantFunc    = "func"
	VariantMethod  = "method"
	VariantValue   = "value" // a const or var block
)

Variant constants for the Go-source chunk kinds. They sit alongside the markdown variants (title/path/narrow/full/body) in the same column.

View Source
const (
	VariantStruct = "struct"
	VariantTrait  = "trait"
	VariantModule = "module"
)

Variant constants for the declaration kinds these languages add. Class, interface, and enum are already declared by the TypeScript chunker and are reused here — a Java class and a TypeScript class are the same concept to a reader filtering search results, so they share a variant.

View Source
const (
	LinkMarkdown = "md"   // [text](dest)
	LinkWiki     = "wiki" // [[target]] / [[target|alias]] / ![[embed]]
	LinkCode     = "code" // `path/to/doc.md` written as inline code, not a link
)

Link kind constants — the syntax an edge was written in.

View Source
const (
	VariantMessage = "message"
	VariantService = "service"
	VariantRPC     = "rpc"
)

Variant constants for the protobuf declaration kinds.

View Source
const (
	VariantClass     = "class"
	VariantInterface = "interface"
	VariantEnum      = "enum"
	VariantFile      = "file"
)

Variant constants for the TypeScript chunk kinds without a Go analogue. Functions, methods, and const/var reuse the func/method/value variants from gosource.go, and a type alias reuses VariantType — they mean the same thing across languages, so search filters and displays stay uniform. VariantFile is the file's own documentation, which VariantPackage is for Go; it is not VariantModule, which names a declared module or namespace in the languages that have one.

View Source
const (
	VariantDocument = "document"
	VariantSection  = "section"
)

Variant constants for the YAML chunk kinds.

View Source
const VariantBlock = "block"

VariantBlock is the chunk variant for one HCL block.

Variables

This section is empty.

Functions

func AnchorSlug

func AnchorSlug(s string) string

AnchorSlug converts heading text into the #fragment GitHub would generate for it, so a link's #section anchor can be validated against real headings. Faithful to github-slugger: lowercase, delete punctuation (keeping '_' and '-'), then turn spaces into hyphens — consecutive hyphens are preserved and there is no length cap. This is deliberately not slugifyHeading, which collapses punctuation runs to a single '-' and caps length for chunk keys; reusing that here mis-slugs punctuation-heavy anchors (`## GET /a/{id}`).

func HeadingLeaf

func HeadingLeaf(breadcrumb string) string

HeadingLeaf returns the leaf heading of a chunker breadcrumb ("# H1 > ## H2 > ### Self" → "Self"): the last segment with its leading `#` markers stripped. Empty for an empty breadcrumb (body/title chunks carry none). Pair with AnchorSlug to recover a section's anchor slug.

func IgnoresFile

func IgnoresFile(content string) bool

IgnoresFile reports whether markdown content opts out of linting entirely with a `semantic-ignore-file` directive, in either comment syntax. Checks that read whole files rather than references — the Contents TOC audit — consult this so a file-level directive means what it says. A directive inside a code block doesn't count, so documenting the syntax can't suppress the file that documents it.

func IsContentsHeading

func IsContentsHeading(s string) bool

IsContentsHeading reports whether heading text names a table of contents, whose list is navigation we don't embed.

func IsMDX added in v0.3.0

func IsMDX(p string) bool

IsMDX reports whether a path is specifically MDX. Callers use it for the handful of behaviours where the JSX flavour differs from plain markdown, so each difference is one explicit check rather than a second file-type concept.

func IsMarkdown

func IsMarkdown(p string) bool

IsMarkdown reports whether a path is a markdown file by extension (.md/.markdown/.mdx, case-insensitive). MDX counts: it chunks, searches, and links as markdown — see byExtension.

func LanguageName

func LanguageName(path string) string

LanguageName returns a path's language name, or "" when it is not indexable.

func LanguageNames

func LanguageNames() []string

LanguageNames returns every filterable language name, sorted — the list `--lang` accepts and the help text prints.

func NormalizeLanguage

func NormalizeLanguage(name string) (string, bool)

NormalizeLanguage resolves an alias to a canonical language name and reports whether it names a language this build can index. A misspelled `--lang` must be an error rather than a filter that silently matches nothing.

func Simplify

func Simplify(s string) string

Simplify reduces markdown to the text worth embedding and displaying: it drops generated TOC blocks and HTML comments, and renders links as just their visible text (`[docs](x)` → `docs`, `![alt](x)` → `alt`, `[[target|label]]` → `label`). Prose, code spans, lists, and emphasis are left as-is. Applied to chunk text at index time so embeddings and snippets aren't padded with URLs, anchors, and markers that carry no meaning.

Types

type Chunk

type Chunk struct {
	Key     string // stable chunk key, e.g. "body/3/overview/narrow", "title"
	Heading string // breadcrumb "# H1 > ## H2 > ### self"; empty for body/title
	Variant string // "path" | "narrow" | "full" | "body" | "title"
	Text    string // text to embed and to display as a snippet
	Line    int    // 1-based source line the chunk starts on (heading line, or 1 for title)
}

Chunk is one retrievable unit of a markdown file. Key is stable across runs for a given body, so re-chunking an unchanged file overwrites the same index rows rather than orphaning old ones.

func Bash

func Bash(content string) []Chunk

Bash parses a shell script and emits a chunk per function and per documented variable assignment, plus the script's header comment when it has one.

func C

func C(content string) []Chunk

C chunks C source: functions, structs, unions, enums, and typedefs.

func CPP

func CPP(content string) []Chunk

CPP chunks C++ source: classes, structs, namespaces, functions, and methods.

func CSharp

func CSharp(content string) []Chunk

CSharp chunks C# source: namespaces, classes, interfaces, structs, records, enums, methods, and properties.

func Document

func Document(content string) []Chunk

Document splits frontmatter off content, synthesizes a title chunk from the frontmatter (when present), and chunks the body by its heading tree. Returns nil when there is nothing embeddable.

func GoSource

func GoSource(content string) []Chunk

GoSource parses Go source and emits a chunk per symbol carrying its breadcrumb ("package foo > Server.Start"), rendered signature, and doc comment. Returns nil when the file doesn't parse into anything usable.

Keys are name-based and the walk is in source order, so re-chunking an unchanged file overwrites the same rows rather than orphaning them — the same stability contract Document relies on.

func HCL

func HCL(content string) []Chunk

HCL parses an HCL file and emits a chunk per block, keyed by the block type and its labels ("resource.aws_s3_bucket.logs"), carrying the comment above it and the block's own attributes.

func Java

func Java(content string) []Chunk

Java chunks Java source: classes, interfaces, enums, records, and methods.

func Lua

func Lua(content string) []Chunk

Lua chunks Lua source: functions, including the dotted and colon paths a module or a method is declared with.

func PHP

func PHP(content string) []Chunk

PHP chunks PHP source: classes, interfaces, traits, enums, functions, and methods.

func Protobuf

func Protobuf(content string) []Chunk

Protobuf parses a .proto file and emits a chunk per message, service, rpc, and enum, each carrying the comment above it. The package declared by the file prefixes every breadcrumb, so a search result says which API surface it came from.

func Python

func Python(content string) []Chunk

Python parses Python source and emits a chunk per module docstring, class, method, function, and documented module-level constant. The retrieval surface is the docstring plus the signature; bodies are not embedded.

func Ruby

func Ruby(content string) []Chunk

Ruby chunks Ruby source: classes, modules, and methods.

func Rust

func Rust(content string) []Chunk

Rust chunks Rust source: structs, enums, traits, modules, functions, and the methods an `impl` block attaches to a type.

func Scala

func Scala(content string) []Chunk

Scala chunks Scala source: classes, objects, traits, enums, and functions.

func TSX

func TSX(content string) []Chunk

TSX chunks .tsx and JavaScript (.js/.jsx/.mjs/.cjs) source with the TSX grammar, which reads `<Foo>` as a JSX element. It is a superset that also parses ordinary, JSX-free JavaScript, so the whole JS family routes here; the symbol kinds it emits (function, class, arrow-const, …) are the ones JS shares with TypeScript, minus the type-only interface/type-alias/enum forms.

func TypeScript

func TypeScript(content string) []Chunk

TypeScript chunks .ts/.mts/.cts source with the plain TypeScript grammar, which reads `<T>` as a type assertion / type parameter rather than JSX.

func YAML

func YAML(content string) []Chunk

YAML parses a YAML file and emits a chunk per document and per top-level key within it. Comments above a document or a key are carried as its documentation.

type Chunker

type Chunker func(content string) []Chunk

Chunker turns a file's full content into retrievable chunks. Document (markdown), GoSource (Go), TypeScript/TSX, Python, Protobuf, SQL, HCL, and Bash all satisfy it; the indexer dispatches on file extension.

type HeadingSpan

type HeadingSpan struct {
	Level        int    // heading level (1 for "#", 2 for "##", …)
	Text         string // raw heading-line text, markers stripped by goldmark, not trimmed
	LineStart    int    // byte offset of the heading line's first char
	ContentStart int    // byte offset just past the heading line, where its content begins
	ATX          bool   // true for "## Foo"; false for a setext heading underlined by ===/---
}

HeadingSpan describes one heading located by HeadingSpans.

func HeadingSpans

func HeadingSpans(src []byte) []HeadingSpan

HeadingSpans parses src as markdown and returns its headings in document order. The chunker (which walks every heading) and the TOC generator (which keeps only ATX headings) share this single parse rather than each re-deriving the AST walk.

type Language

type Language struct {
	Name  string
	Chunk Chunker
}

Language is one indexable language: the name a user types after `--lang`, and the chunker that splits its files.

func LanguageFor

func LanguageFor(path string) (Language, bool)

LanguageFor returns the language for a path, and whether the path is indexable at all.

type Link struct {
	Target string // raw destination, #anchor/?query/|alias stripped
	Anchor string // #section fragment, without the leading '#'; empty if none
	Line   int    // 1-based source line the link appears on
	Kind   string // LinkMarkdown | LinkWiki | LinkCode
}

Link is one outbound reference from a document. Target is the raw destination as written (a relative path or a wikilink target); resolving it to an actual indexed file happens in the graph layer, which has the whole file set. Anchor is the #section fragment, if any, kept apart from Target so the graph/lint layers can validate it against the target file's headings. External URLs (http, mailto, …) and pure #anchors are not emitted — only candidate vault-internal references become edges. LinkCode references are doc or source-code paths written as inline code; they aren't real edges (the graph drops them) but the lint layer flags them as pointers that could be links.

func Links(name, content string) []Link

Links extracts outbound document references from markdown content. Inline `[text](dest)` links come from the goldmark AST, so links written inside code blocks are naturally ignored; `[[wikilinks]]` come from a source scan (goldmark leaves them as literal text) that skips matches inside code blocks. Inline-code spans that look like doc or source-code paths (`docs/foo.md`, `internal/foo/bar.go`) are also emitted as LinkCode — not edges, but candidates the lint layer surfaces. In an MDX file, a JSX element's href (`<Card href="/x" />`) is also emitted, as an ordinary LinkMarkdown edge — it is a link, just written as an attribute. name selects that behaviour by extension; a plain .md file yields exactly the edges it did before, so an HTML `<a href>` in one is still left alone. Frontmatter is stripped first so line numbers map to the file. A `semantic-ignore` directive suppresses references on its line (see applyIgnores).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL