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
- func AnchorSlug(s string) string
- func HeadingLeaf(breadcrumb string) string
- func IgnoresFile(content string) bool
- func IsContentsHeading(s string) bool
- func IsMDX(p string) bool
- func IsMarkdown(p string) bool
- func LanguageName(path string) string
- func LanguageNames() []string
- func NormalizeLanguage(name string) (string, bool)
- func Simplify(s string) string
- type Chunk
- func Bash(content string) []Chunk
- func C(content string) []Chunk
- func CPP(content string) []Chunk
- func CSharp(content string) []Chunk
- func Document(content string) []Chunk
- func GoSource(content string) []Chunk
- func HCL(content string) []Chunk
- func Java(content string) []Chunk
- func Lua(content string) []Chunk
- func PHP(content string) []Chunk
- func Protobuf(content string) []Chunk
- func Python(content string) []Chunk
- func Ruby(content string) []Chunk
- func Rust(content string) []Chunk
- func Scala(content string) []Chunk
- func TSX(content string) []Chunk
- func TypeScript(content string) []Chunk
- func YAML(content string) []Chunk
- type Chunker
- type HeadingSpan
- type Language
- type Link
Constants ¶
const ( VariantTitle = "title" VariantPath = "path" VariantNarrow = "narrow" VariantFull = "full" VariantBody = "body" )
Variant constants for the chunk kinds this package emits.
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.
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.
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.
const ( VariantMessage = "message" VariantService = "service" VariantRPC = "rpc" )
Variant constants for the protobuf declaration kinds.
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.
const ( VariantDocument = "document" VariantSection = "section" )
Variant constants for the YAML chunk kinds.
const VariantBlock = "block"
VariantBlock is the chunk variant for one HCL block.
Variables ¶
This section is empty.
Functions ¶
func AnchorSlug ¶
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 ¶
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 ¶
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 ¶
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
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 ¶
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 ¶
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 ¶
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 ¶
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`, `[[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 ¶
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 CSharp ¶
CSharp chunks C# source: namespaces, classes, interfaces, structs, records, enums, methods, and properties.
func Document ¶
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 ¶
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 ¶
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 Lua ¶
Lua chunks Lua source: functions, including the dotted and colon paths a module or a method is declared with.
func Protobuf ¶
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 ¶
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 Rust ¶
Rust chunks Rust source: structs, enums, traits, modules, functions, and the methods an `impl` block attaches to a type.
func TSX ¶
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 ¶
TypeScript chunks .ts/.mts/.cts source with the plain TypeScript grammar, which reads `<T>` as a type assertion / type parameter rather than JSX.
type Chunker ¶
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 ¶
Language is one indexable language: the name a user types after `--lang`, and the chunker that splits its files.
func LanguageFor ¶
LanguageFor returns the language for a path, and whether the path is indexable at all.
type Link ¶
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 ¶
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).