markdown

package
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Jan 1, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package markdown provides a markdown parser and AST for spectr specifications.

compat.go provides line-by-line matchers for compatibility with the old regex package API. These functions work on single lines and are efficient for line-by-line processing during migration from regex-based parsing.

These helpers use simple string operations instead of regex for better performance and maintainability.

Package markdown provides a token-based lexer and parser for CommonMark subset plus Spectr-specific extensions. It provides a robust, maintainable, and feature-rich approach for parsing Spectr specification files.

Architecture Overview

The package uses a two-phase parsing architecture:

  1. Lexer: Tokenizes input into fine-grained tokens (delimiters, text, whitespace)
  2. Parser: Consumes tokens to build an immutable AST

This separation provides clear concerns, testable components, and flexibility for error recovery.

Source []byte --> Lexer --> []Token --> Parser --> AST (Node tree)
                              |                      |
                              v                      v
                         TokenError            ParseError

Supported CommonMark Subset

The parser supports a useful subset of CommonMark:

  • Headers: ATX-style headers (H1-H6) using # prefix
  • Lists: Unordered (-, *, +), ordered (1.), task checkboxes (- [ ], - [x])
  • Code: Fenced code blocks (``` and ~~~), inline code (`)
  • Emphasis: Bold (**, __), italic (*, _), strikethrough (~~)
  • Links: Inline [text](url), reference [text][ref] with [ref]: url definitions
  • Block elements: Paragraphs, blockquotes (>)

Spectr-Specific Extensions

Beyond CommonMark, the parser recognizes Spectr patterns:

  • Wikilinks: [[spec-name]], [[spec-name|display text]], [[target#anchor]]
  • Requirement headers: ### Requirement: Name
  • Scenario headers: #### Scenario: Description
  • WHEN/THEN/AND bullets: - **WHEN** condition, - **THEN** result
  • Delta sections: ## ADDED Requirements, ## MODIFIED Requirements, etc.

Key Types

Token represents a lexical unit with position information:

type Token struct {
    Type    TokenType  // Token classification
    Start   int        // Byte offset from source start
    End     int        // Byte offset past last byte (exclusive)
    Source  []byte     // Zero-copy slice into original source
    Message string     // Error message (only for TokenError)
}

Node is the interface for all AST nodes:

type Node interface {
    NodeType() NodeType        // Type classification
    Span() (start, end int)    // Byte offset range
    Hash() uint64              // Content hash for identity/caching
    Source() []byte            // Original source text
    Children() []Node          // Child nodes (immutable)
}

Typed node structs implement the Node interface with type-specific getters:

  • NodeDocument: Root document container
  • NodeSection: Header sections with Level() and Title() getters
  • NodeRequirement: Spectr requirement with Name() getter
  • NodeScenario: Spectr scenario with Name() getter
  • NodeParagraph: Paragraph container
  • NodeList: List container with Ordered() getter
  • NodeListItem: List item with Checked() and Keyword() getters
  • NodeCodeBlock: Fenced code with Language() and Content() getters
  • NodeBlockquote: Blockquote container
  • NodeText: Plain text content
  • NodeStrong: Bold emphasis
  • NodeEmphasis: Italic emphasis
  • NodeStrikethrough: Strikethrough text
  • NodeCode: Inline code
  • NodeLink: Link with URL() and Title() getters
  • NodeWikilink: Wikilink with Target(), Display(), and Anchor() getters

ParseError represents a parse error with location:

type ParseError struct {
    Offset   int           // Byte offset where error occurred
    Message  string        // Human-readable error description
    Expected []TokenType   // What tokens would have been valid
}

Key Functions

Parse is the main entry point for parsing markdown:

func Parse(source []byte) (Node, []ParseError)

Parse is stateless and safe for concurrent calls. It returns the root document node and any errors encountered. Even with errors, a partial AST is returned.

ParseIncremental enables efficient reparsing after edits:

func ParseIncremental(oldTree Node, oldSource, newSource []byte) (Node, []ParseError)

ParseIncremental computes the diff between old and new source, identifies affected regions, reparses only changed sections, and reuses unchanged subtrees via content hash matching. This provides tree-sitter style incremental parsing.

Usage Examples

Basic parsing:

source := []byte("# Hello\n\nThis is a **test**.")
root, errs := markdown.Parse(source)
if len(errs) > 0 {
    for _, err := range errs {
        fmt.Printf("Error at offset %d: %s\n", err.Offset, err.Message)
    }
}
// Process AST...

Using the visitor pattern:

type RequirementCollector struct {
    markdown.BaseVisitor
    Requirements []string
}

func (c *RequirementCollector) VisitRequirement(n *markdown.NodeRequirement) error {
    c.Requirements = append(c.Requirements, n.Name())
    return nil
}

collector := &RequirementCollector{}
markdown.Walk(root, collector)

Using queries:

// Find all requirements
reqs := markdown.Find(root, markdown.IsType[*markdown.NodeRequirement]())

// Find requirement by name
req := markdown.FindFirst(root, markdown.And(
    markdown.IsType[*markdown.NodeRequirement](),
    markdown.HasName("My Requirement"),
))

Using transforms:

// Rename a requirement
newRoot, err := markdown.Transform(root, markdown.RenameRequirement("OldName", "NewName"))

Incremental parsing:

// Initial parse
root1, _ := markdown.Parse(source1)

// After edit, reparse incrementally
root2, _ := markdown.ParseIncremental(root1, source1, source2)

Error Handling

The parser uses a collected error approach rather than fail-fast:

  • All errors are collected during parsing
  • Parsing continues via error recovery (skip to next sync point)
  • A partial AST is always returned, even with errors
  • Maximum 100 errors before aborting (configurable)

Errors store byte offsets. Use LineIndex for line/column conversion:

idx := markdown.NewLineIndex(source)
for _, err := range errs {
    pos := idx.PositionAt(err.Offset)
    fmt.Printf("Line %d, Column %d: %s\n", pos.Line, pos.Column, err.Message)
}

Position Information

All tokens and nodes track byte offsets into the original source. This is compact and efficient. For line/column display, use LineIndex:

idx := markdown.NewLineIndex(source)
line, col := idx.LineCol(offset)  // 1-based line numbers

For frequent position queries on an AST, use PositionIndex which builds an interval tree for O(log n) lookups:

pidx := markdown.NewPositionIndex(root)
node := pidx.NodeAt(offset)         // Innermost node at offset
nodes := pidx.NodesAt(offset)       // All nodes containing offset
nodes := pidx.NodesInRange(10, 50)  // All nodes overlapping range

Thread Safety

Parse and ParseIncremental are safe for concurrent calls (no shared state). AST nodes are immutable after creation. Object pools use sync.Pool internally. LineIndex and PositionIndex are safe for concurrent reads after construction.

Performance

The package is optimized for performance:

  • Zero-copy source views ([]byte slices into original input)
  • Object pooling for tokens and nodes via sync.Pool
  • Content hashing for efficient subtree comparison
  • Lazy line index construction
  • Interval tree for O(log n) position queries
  • Incremental parsing reuses unchanged subtrees

Non-Goals

This package intentionally does not support:

  • Full CommonMark compliance (focused subset for Spectr)
  • Tables
  • HTML passthrough
  • Setext-style headers (underlined with === or ---)
  • GFM extensions beyond task checkboxes and strikethrough

Index

Constants

View Source
const DefaultMaxErrors = 100

DefaultMaxErrors is the maximum number of parse errors before aborting.

Variables

View Source
var SkipChildren = errors.New("skip children")

ErrSkipChildren is a sentinel error that can be returned from a visitor method to skip traversal of the current node's children. The traversal will continue with the next sibling. This is NOT treated as an actual error.

Functions

func AffectedBlockRegion

func AffectedBlockRegion(
	source []byte,
	edit EditRegion,
) (start, end int)

AffectedBlockRegion identifies the block-level region affected by an edit. This is useful for determining what portion of the document needs reparsing. It expands the edit region to block boundaries (blank lines, headers, etc.)

func ContainsKeyword

func ContainsKeyword(
	line string,
) (keyword string, ok bool)

ContainsKeyword checks if a line contains one of the Spectr keywords (WHEN, THEN, AND, GIVEN) typically used in scenario descriptions. Returns the keyword found and true, or empty and false.

func Count

func Count(root Node, pred Predicate) int

Count returns the number of nodes matching pred without allocating a slice. Traverses the entire tree and increments a counter for each match.

func CountDeltaChanges

func CountDeltaChanges(delta *Delta) int

CountDeltaChanges returns the total number of changes in a delta.

func CountLeadingSpaces

func CountLeadingSpaces(line string) int

CountLeadingSpaces returns the number of leading space characters in a line. Tabs are counted as 1 character (not expanded).

func CountRequirements

func CountRequirements(content []byte) int

CountRequirements returns the total number of requirements in the content.

func CountScenarios

func CountScenarios(content []byte) int

CountScenarios returns the total number of scenarios in the content.

func CountWikilinks(content []byte) int

CountWikilinks returns the total number of wikilinks in the content.

func DisablePoolStats

func DisablePoolStats()

DisablePoolStats disables pool statistics tracking.

func EnablePoolStats

func EnablePoolStats()

EnablePoolStats enables pool statistics tracking. This adds some overhead to pool operations.

func Exists

func Exists(root Node, pred Predicate) bool

Exists returns true if any node matches pred. Uses short-circuit evaluation: stops on first match.

func ExtractDeltaContent

func ExtractDeltaContent(
	content []byte,
	deltaType DeltaType,
) []byte

ExtractDeltaContent extracts content from a specific delta section type. This is useful when you need the raw markdown content of a delta section.

func ExtractHeaderLevel

func ExtractHeaderLevel(line string) int

ExtractHeaderLevel returns the header level (1-6) for a line that starts with hash characters followed by a space. Returns 0 if not a valid header.

Example:

ExtractHeaderLevel("# Title")    // returns 1
ExtractHeaderLevel("### Section") // returns 3
ExtractHeaderLevel("Not a header") // returns 0

func ExtractHeaderText

func ExtractHeaderText(line string) string

ExtractHeaderText returns the header text without the "#" prefix and leading/trailing whitespace. Returns empty string if not a valid header.

Example:

ExtractHeaderText("## My Section")  // returns "My Section"
ExtractHeaderText("# Title  ")       // returns "Title"

func ExtractListMarker

func ExtractListMarker(
	line string,
) (marker string, ok bool)

ExtractListMarker extracts the list marker from a line if present. Returns the marker string and true if found, or empty and false otherwise. For unordered lists, returns "-", "*", or "+". For ordered lists, returns the number with dot (e.g., "1.", "23.").

func ExtractSections

func ExtractSections(
	content []byte,
) map[string]*Section

ExtractSections parses content and returns a map of section name to Section. The keys are the exact section titles (case-preserved).

func FindAddedRequirements

func FindAddedRequirements(
	content []byte,
) []string

FindAddedRequirements returns the names of all added requirements.

func FindAllDeltaSections

func FindAllDeltaSections(
	content []byte,
) map[DeltaType]*Section

FindAllDeltaSections returns all delta sections in the content. Returns a map from delta type to the section content.

func FindAllH3Requirements

func FindAllH3Requirements(
	content string,
) []string

FindAllH3Requirements finds all requirement headers in content and returns their names. This function scans for "### Requirement: Name" patterns and extracts the names.

Example:

content := "### Requirement: Auth\n\nContent\n\n### Requirement: Logging\n"
names := FindAllH3Requirements(content)
// names = ["Auth", "Logging"]

func FindByType

func FindByType[T Node](root Node) []T

FindByType returns all nodes of type T in the AST. Results are properly typed and in traversal order. The type parameter T must implement the Node interface.

func FindDeltaSection

func FindDeltaSection(
	content []byte,
	deltaType DeltaType,
) string

FindDeltaSection returns the content of a specific delta section type. It searches for sections matching the given delta type (ADDED, MODIFIED, REMOVED, RENAMED). Returns empty string if no matching section is found.

func FindDeltaSectionContent

func FindDeltaSectionContent(
	content []byte,
	deltaType DeltaType,
) string

FindDeltaSectionContent extracts the content from a delta section (ADDED, MODIFIED, REMOVED, RENAMED) without the header line. This is a compatibility function that matches the behavior of the old regex.FindDeltaSectionContent function.

Returns the content between the section header and the next H2 header, or empty string if the section is not found.

Example:

content := `# Delta
## ADDED Requirements
### Requirement: New Feature
Content here.
## MODIFIED Requirements
...`
FindDeltaSectionContent([]byte(content), DeltaAdded)
// returns "\n### Requirement: New Feature\nContent here.\n"

func FindFirstByType

func FindFirstByType[T Node](root Node) T

FindFirstByType returns the first node of type T, or nil if none exists. The type parameter T must implement the Node interface.

func FindH2RequirementsSection

func FindH2RequirementsSection(
	content string,
) []int

FindH2RequirementsSection finds the "## Requirements" section header in content. Returns the start and end indices of the header match, or nil if not found. The end index points to the end of the header line (after "## Requirements").

Example:

indices := FindH2RequirementsSection("# Title\n\n## Requirements\n\nContent")
// indices = [9, 24] (pointing to "## Requirements")

func FindModifiedRequirements

func FindModifiedRequirements(
	content []byte,
) []string

FindModifiedRequirements returns the names of all modified requirements.

func FindNextH2Section

func FindNextH2Section(
	content string,
	startPos int,
) []int

FindNextH2Section finds the next "## " header in content starting from startPos. Returns the start and end indices of the header match relative to the beginning of the content, or nil if no H2 header is found after startPos.

Example:

content := "## First\n\nSome text\n\n## Second\n"
indices := FindNextH2Section(content, 9) // Search after "## First\n"
// indices = [22, 31] (pointing to "## Second")

func FindRemovedRequirements

func FindRemovedRequirements(
	content []byte,
) []string

FindRemovedRequirements returns the names of all removed requirements.

func FindRenamedPairs

func FindRenamedPairs(
	content []byte,
) map[string]string

FindRenamedPairs extracts all FROM/TO pairs from the content. This is a convenience function for getting just the rename mappings.

func GetDeltaRequirementNames

func GetDeltaRequirementNames(
	delta *Delta,
) []string

GetDeltaRequirementNames returns all requirement names affected by the delta. This includes added, modified, removed, and renamed requirements.

func GetDeltaSummary

func GetDeltaSummary(delta *Delta) string

GetDeltaSummary returns a human-readable summary of delta changes.

func GetRequirementNames

func GetRequirementNames(
	content []byte,
) []string

GetRequirementNames returns the names of all requirements in the content.

func GetSectionContent

func GetSectionContent(
	content []byte,
	sectionName string,
) string

GetSectionContent returns the text content of a section by name. Returns empty string if section not found.

func GetSectionNames

func GetSectionNames(content []byte) []string

GetSectionNames returns the names of all sections in the content.

func GetWikilinkTargetType

func GetWikilinkTargetType(
	target, projectRoot string,
) string

GetWikilinkTargetType returns the type of target a wikilink refers to. Returns "spec", "change", or "unknown".

func HasDeltaSection

func HasDeltaSection(
	content []byte,
	deltaType DeltaType,
) bool

HasDeltaSection checks if the content contains a specific delta section type.

func HasRequirement

func HasRequirement(
	content []byte,
	name string,
) bool

HasRequirement checks if a requirement with the given name exists.

func HasSection

func HasSection(
	content []byte,
	name string,
) bool

HasSection checks if a section with the given name exists (case-insensitive).

func IsBlankLine

func IsBlankLine(line string) bool

IsBlankLine checks if a line is empty or contains only whitespace.

func IsBlockquote

func IsBlockquote(line string) bool

IsBlockquote checks if a line starts with a blockquote marker (>).

func IsCodeFence

func IsCodeFence(line string) (isFence bool, delimiter rune)

IsCodeFence checks if a line starts a code fence (``` or ~~~). Returns true and the fence character ('`' or '~') if it's a fence, or false and 0 otherwise.

func IsH2Header

func IsH2Header(line string) bool

IsH2Header checks if a line starts with "## " (any H2 header).

func IsH3Header

func IsH3Header(line string) bool

IsH3Header checks if a line starts with "### " (any H3 header).

func IsH4Header

func IsH4Header(line string) bool

IsH4Header checks if a line starts with "#### " (any H4 header).

func IsHorizontalRule

func IsHorizontalRule(line string) bool

IsHorizontalRule checks if a line is a horizontal rule (---, ***, ___).

func IsListItem

func IsListItem(line string) bool

IsListItem checks if a line starts with a list marker. Supports unordered markers (-, *, +) and ordered markers (1., 2., etc.) after optional leading whitespace.

func IsTaskChecked

func IsTaskChecked(state rune) bool

IsTaskChecked returns true if the checkbox state indicates completion. Accepts 'x' or 'X' as checked states.

func ListWikilinkTargets

func ListWikilinkTargets(
	content []byte,
) []string

ListWikilinkTargets extracts all wikilink targets from content. Returns a slice of unique target strings.

func MatchAnyRenamedFrom

func MatchAnyRenamedFrom(
	line string,
) (name string, ok bool)

MatchAnyRenamedFrom tries both backtick and non-backtick FROM formats. Returns the requirement name and true if either format matches. Tries backtick format first, then falls back to non-backtick.

func MatchAnyRenamedTo

func MatchAnyRenamedTo(
	line string,
) (name string, ok bool)

MatchAnyRenamedTo tries both backtick and non-backtick TO formats. Returns the requirement name and true if either format matches. Tries backtick format first, then falls back to non-backtick.

func MatchAnySection added in v0.0.9

func MatchAnySection(
	line string,
) (name, number string, ok bool)

MatchAnySection parses any H2 section header from tasks.md format. Accepts both numbered and unnumbered formats:

  • "## 1. Setup" -> name="Setup", number="1", ok=true
  • "## Implementation" -> name="Implementation", number="", ok=true

Returns the section name, optional number, and true if matched.

func MatchH2DeltaSection

func MatchH2DeltaSection(
	line string,
) (deltaType string, ok bool)

MatchH2DeltaSection checks if a line is a delta section header (## ADDED|MODIFIED|REMOVED|RENAMED Requirements). Returns the delta type and true if matched, or empty string and false.

Example:

deltaType, ok := MatchH2DeltaSection("## ADDED Requirements")
// deltaType = "ADDED", ok = true

func MatchH2SectionHeader

func MatchH2SectionHeader(
	line string,
) (name string, ok bool)

MatchH2SectionHeader checks if a line is an H2 section header and extracts the name. Returns the section name and true if matched, or empty string and false otherwise. This is a compatibility wrapper matching the old regex API.

Example:

name, ok := MatchH2SectionHeader("## Purpose")
// name = "Purpose", ok = true

func MatchNumberedSection

func MatchNumberedSection(
	line string,
) (name string, ok bool)

MatchNumberedSection parses a numbered section header from tasks.md format. Format: "## 1. Section Name" Returns the section name (without number prefix) and true if matched, or empty string and false otherwise.

Example:

name, ok := MatchNumberedSection("## 1. Core Accept Command")
// name = "Core Accept Command", ok = true

func MatchRenamedFrom

func MatchRenamedFrom(
	line string,
) (name string, ok bool)

MatchRenamedFrom checks if a line matches the backtick-wrapped FROM format for renamed requirements. Returns the requirement name and true if matched, or empty string and false otherwise.

Expected format: "- FROM: `### Requirement: Old Name`"

Example:

name, ok := MatchRenamedFrom("- FROM: `### Requirement: Old Name`")
// name = "Old Name", ok = true

func MatchRenamedFromAlt

func MatchRenamedFromAlt(
	line string,
) (name string, ok bool)

MatchRenamedFromAlt checks if a line matches the non-backtick FROM format for renamed requirements. Returns the requirement name and true if matched, or empty string and false otherwise.

Expected format: "- FROM: ### Requirement: Old Name"

Example:

name, ok := MatchRenamedFromAlt("- FROM: ### Requirement: Old Name")
// name = "Old Name", ok = true

func MatchRenamedTo

func MatchRenamedTo(
	line string,
) (name string, ok bool)

MatchRenamedTo checks if a line matches the backtick-wrapped TO format for renamed requirements. Returns the requirement name and true if matched, or empty string and false otherwise.

Expected format: "- TO: `### Requirement: New Name`"

Example:

name, ok := MatchRenamedTo("- TO: `### Requirement: New Name`")
// name = "New Name", ok = true

func MatchRenamedToAlt

func MatchRenamedToAlt(
	line string,
) (name string, ok bool)

MatchRenamedToAlt checks if a line matches the non-backtick TO format for renamed requirements. Returns the requirement name and true if matched, or empty string and false otherwise.

Expected format: "- TO: ### Requirement: New Name"

Example:

name, ok := MatchRenamedToAlt("- TO: ### Requirement: New Name")
// name = "New Name", ok = true

func MatchRequirementHeader

func MatchRequirementHeader(
	line string,
) (name string, ok bool)

MatchRequirementHeader checks if a line is a requirement header ("### Requirement: Name") and extracts the name. Returns the requirement name and true if matched, or empty string and false. This function allows flexible whitespace between ### and "Requirement:" and after the colon, matching the behavior of the original regex.

Example:

name, ok := MatchRequirementHeader("### Requirement: User Authentication")
// name = "User Authentication", ok = true

func MatchScenarioHeader

func MatchScenarioHeader(
	line string,
) (name string, ok bool)

MatchScenarioHeader checks if a line is a scenario header ("#### Scenario: Name") and extracts the name. Returns the scenario name and true if matched, or empty string and false. This function allows flexible whitespace between #### and "Scenario:" and after the colon, matching the behavior of the original regex.

Example:

name, ok := MatchScenarioHeader("#### Scenario: User logs in")
// name = "User logs in", ok = true

func MatchTaskCheckbox

func MatchTaskCheckbox(
	line string,
) (status rune, ok bool)

MatchTaskCheckbox checks if a line contains a task checkbox and extracts the state. The line must start with optional whitespace, followed by "- [x]" or "- [ ]". Returns the checkbox state (' ' for unchecked, 'x' or 'X' for checked) and true if matched, or 0 and false otherwise.

Example:

state, ok := MatchTaskCheckbox("- [ ] Unchecked task")
// state = ' ', ok = true

state, ok := MatchTaskCheckbox("- [x] Checked task")
// state = 'x', ok = true

func MergeDelta

func MergeDelta(merged, delta *Delta)

MergeDelta merges delta changes into the merged delta. This is useful for combining multiple delta files.

func NormalizeMultipleNewlines

func NormalizeMultipleNewlines(s string) string

NormalizeMultipleNewlines collapses sequences of 3 or more consecutive newlines into exactly 2 newlines. This replaces the inline regex pattern `\n{3,}` with a string-based implementation.

Example:

result := NormalizeMultipleNewlines("text\n\n\n\nmore")
// result = "text\n\nmore"

func Parse

func Parse(source []byte) (Node, []ParseError)

Parse transforms source bytes into an immutable AST. It returns the root document node and any parse errors encountered. This function is stateless and safe for concurrent calls.

func ParseDelta

func ParseDelta(
	content []byte,
) (*Delta, []ParseError)

ParseDelta parses markdown content as a delta specification and returns a Delta with categorized requirement changes.

func ParseIncremental

func ParseIncremental(
	oldTree Node,
	oldSource, newSource []byte,
) (Node, []ParseError)

ParseIncremental performs incremental parsing by reusing parts of the old AST. It computes the diff between oldSource and newSource, identifies affected regions, and attempts to reuse unchanged subtrees from oldTree.

This is a tree-sitter style incremental parser that: 1. Computes diff between old and new source 2. Identifies affected region(s) 3. Reuses unchanged subtrees (matched by content hash) 4. Adjusts offsets for nodes after edit point 5. Reparses only the changed regions

For large changes (>20% of file), falls back to full reparse.

func ParseIncrementalWithState

func ParseIncrementalWithState(
	state *IncrementalParseState,
	oldTree Node,
	oldSource, newSource []byte,
) (Node, []ParseError, *IncrementalParseState)

ParseIncrementalWithState performs incremental parsing using cached state. This is more efficient than ParseIncremental when the state is available.

func ParseSpec

func ParseSpec(
	content []byte,
) (*Spec, []ParseError)

ParseSpec parses markdown content and returns a Spec with structured access to sections, requirements, and scenarios.

func Print

func Print(node Node) []byte

Print renders the AST node to normalized markdown. It returns the rendered markdown as a byte slice. The output uses consistent formatting (ATX headers, dash bullets) with minimal whitespace.

func PrintTo

func PrintTo(w io.Writer, node Node) error

PrintTo renders the AST node to the provided io.Writer. It streams output without buffering the entire result. Returns any write errors encountered.

func PutChildren

func PutChildren(c []Node)

PutChildren returns a children slice to the appropriate pool. The slice is cleared before being returned to prevent memory leaks.

func PutNode

func PutNode(n Node)

PutNode returns a node to the appropriate pool based on its type. The node should not be used after this call.

func PutToken

func PutToken(t *Token)

PutToken returns a Token to the pool after clearing its fields. The token should not be used after this call.

func ResetPoolStats

func ResetPoolStats()

ResetPoolStats resets all pool statistics to zero.

func ResolveWikilink(
	target, projectRoot string,
) (path string, exists bool)

ResolveWikilink resolves a wikilink target to a file path within the project. It follows the Spectr resolution rules:

  1. First check spectr/specs/{target}/spec.md
  2. Then check spectr/changes/{target}/proposal.md
  3. If target contains "/", treat first segment as directory type

Returns the resolved path and whether the file exists. The projectRoot should be the root directory containing the spectr/ folder.

func ResolveWikilinkWithAnchor

func ResolveWikilinkWithAnchor(
	target, anchor, projectRoot string,
) (path string, anchorValid bool, err error)

ResolveWikilinkWithAnchor resolves a wikilink target and validates anchor. It first resolves the target path using ResolveWikilink, then parses the target file to validate that the anchor exists as a header.

Returns:

  • path: the resolved file path
  • anchorValid: true if anchor exists in target (or if no anchor given)
  • err: any error encountered during resolution or file reading

Anchor matching rules:

  • Anchors are matched case-insensitively
  • "Requirement: Name" matches ### Requirement: Name headers
  • "Scenario: Name" matches #### Scenario: Name headers
  • Plain text matches any header containing that text

func TrimLeadingHashes

func TrimLeadingHashes(line string) string

TrimLeadingHashes removes leading hash characters and the following space from a header line, returning the header content.

func ValidateRenamed

func ValidateRenamed(delta *Delta) []string

ValidateRenamed checks that all renamed requirements have both From and To values. Returns the names of any incomplete rename operations.

func ValidateWikilinkTarget

func ValidateWikilinkTarget(
	target, projectRoot string,
) error

ValidateWikilinkTarget checks if a single wikilink target is valid. This is a convenience function for validating individual targets.

func Walk

func Walk(node Node, v Visitor) error

Walk traverses the AST in pre-order depth-first order, calling the appropriate visitor method for each node. It handles the traversal logic including child recursion and error handling.

If a visitor method returns SkipChildren, the children of that node are skipped but traversal continues with the next sibling.

If a visitor method returns any other non-nil error, traversal stops immediately and that error is returned.

Walk safely handles nil nodes by returning nil without calling any visitor methods.

func WalkEnterLeave

func WalkEnterLeave(
	node Node,
	v EnterLeaveVisitor,
) error

WalkEnterLeave traverses the AST calling Enter methods before visiting children and Leave methods after visiting children.

If an Enter method returns SkipChildren, the children are skipped but the corresponding Leave method is still called.

If an Enter method returns any other non-nil error, traversal stops immediately and the Leave method is NOT called.

If a Leave method returns a non-nil error, traversal stops immediately.

WalkEnterLeave safely handles nil nodes by returning nil.

func WalkWithContext

func WalkWithContext(
	node Node,
	v ContextVisitor,
) error

WalkWithContext traverses the AST like Walk but provides context information including parent node access to the visitor.

Types

type AdjustedNode

type AdjustedNode struct {
	// contains filtered or unexported fields
}

AdjustedNode represents a node with adjusted offsets for incremental parsing. Used when reusing nodes from the old tree that appear after an edit point.

type BaseContextVisitor

type BaseContextVisitor struct{}

BaseContextVisitor provides no-op defaults for all ContextVisitor methods.

func (BaseContextVisitor) VisitBlockquoteWithContext

func (BaseContextVisitor) VisitBlockquoteWithContext(
	*NodeBlockquote,
	*VisitorContext,
) error

VisitBlockquoteWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitCodeBlockWithContext

func (BaseContextVisitor) VisitCodeBlockWithContext(
	*NodeCodeBlock,
	*VisitorContext,
) error

VisitCodeBlockWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitCodeWithContext

func (BaseContextVisitor) VisitCodeWithContext(
	*NodeCode,
	*VisitorContext,
) error

VisitCodeWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitDocumentWithContext

func (BaseContextVisitor) VisitDocumentWithContext(
	*NodeDocument,
	*VisitorContext,
) error

VisitDocumentWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitEmphasisWithContext

func (BaseContextVisitor) VisitEmphasisWithContext(
	*NodeEmphasis,
	*VisitorContext,
) error

VisitEmphasisWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitLinkDefWithContext

func (BaseContextVisitor) VisitLinkDefWithContext(
	*NodeLinkDef,
	*VisitorContext,
) error

VisitLinkDefWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitLinkWithContext

func (BaseContextVisitor) VisitLinkWithContext(
	*NodeLink,
	*VisitorContext,
) error

VisitLinkWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitListItemWithContext

func (BaseContextVisitor) VisitListItemWithContext(
	*NodeListItem,
	*VisitorContext,
) error

VisitListItemWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitListWithContext

func (BaseContextVisitor) VisitListWithContext(
	*NodeList,
	*VisitorContext,
) error

VisitListWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitParagraphWithContext

func (BaseContextVisitor) VisitParagraphWithContext(
	*NodeParagraph,
	*VisitorContext,
) error

VisitParagraphWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitRequirementWithContext

func (BaseContextVisitor) VisitRequirementWithContext(
	*NodeRequirement,
	*VisitorContext,
) error

VisitRequirementWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitScenarioWithContext

func (BaseContextVisitor) VisitScenarioWithContext(
	*NodeScenario,
	*VisitorContext,
) error

VisitScenarioWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitSectionWithContext

func (BaseContextVisitor) VisitSectionWithContext(
	*NodeSection,
	*VisitorContext,
) error

VisitSectionWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitStrikethroughWithContext

func (BaseContextVisitor) VisitStrikethroughWithContext(
	*NodeStrikethrough,
	*VisitorContext,
) error

VisitStrikethroughWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitStrongWithContext

func (BaseContextVisitor) VisitStrongWithContext(
	*NodeStrong,
	*VisitorContext,
) error

VisitStrongWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitTextWithContext

func (BaseContextVisitor) VisitTextWithContext(
	*NodeText,
	*VisitorContext,
) error

VisitTextWithContext is a no-op that returns nil.

func (BaseContextVisitor) VisitWikilinkWithContext

func (BaseContextVisitor) VisitWikilinkWithContext(
	*NodeWikilink,
	*VisitorContext,
) error

VisitWikilinkWithContext is a no-op that returns nil.

type BaseEnterLeaveVisitor

type BaseEnterLeaveVisitor struct{}

BaseEnterLeaveVisitor provides no-op default implementations for all EnterLeaveVisitor methods. Embed this struct in custom visitors to only override the methods you need.

func (BaseEnterLeaveVisitor) EnterBlockquote

func (BaseEnterLeaveVisitor) EnterBlockquote(
	*NodeBlockquote,
) error

EnterBlockquote is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterCode

func (BaseEnterLeaveVisitor) EnterCode(
	*NodeCode,
) error

EnterCode is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterCodeBlock

func (BaseEnterLeaveVisitor) EnterCodeBlock(
	*NodeCodeBlock,
) error

EnterCodeBlock is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterDocument

func (BaseEnterLeaveVisitor) EnterDocument(
	*NodeDocument,
) error

EnterDocument is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterEmphasis

func (BaseEnterLeaveVisitor) EnterEmphasis(
	*NodeEmphasis,
) error

EnterEmphasis is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterLink(
	*NodeLink,
) error

EnterLink is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterLinkDef

func (BaseEnterLeaveVisitor) EnterLinkDef(
	*NodeLinkDef,
) error

EnterLinkDef is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterList

func (BaseEnterLeaveVisitor) EnterList(
	*NodeList,
) error

EnterList is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterListItem

func (BaseEnterLeaveVisitor) EnterListItem(
	*NodeListItem,
) error

EnterListItem is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterParagraph

func (BaseEnterLeaveVisitor) EnterParagraph(
	*NodeParagraph,
) error

EnterParagraph is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterRequirement

func (BaseEnterLeaveVisitor) EnterRequirement(
	*NodeRequirement,
) error

EnterRequirement is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterScenario

func (BaseEnterLeaveVisitor) EnterScenario(
	*NodeScenario,
) error

EnterScenario is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterSection

func (BaseEnterLeaveVisitor) EnterSection(
	*NodeSection,
) error

EnterSection is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterStrikethrough

func (BaseEnterLeaveVisitor) EnterStrikethrough(
	*NodeStrikethrough,
) error

EnterStrikethrough is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterStrong

func (BaseEnterLeaveVisitor) EnterStrong(
	*NodeStrong,
) error

EnterStrong is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterText

func (BaseEnterLeaveVisitor) EnterText(
	*NodeText,
) error

EnterText is a no-op that returns nil.

func (BaseEnterLeaveVisitor) EnterWikilink(
	*NodeWikilink,
) error

EnterWikilink is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveBlockquote

func (BaseEnterLeaveVisitor) LeaveBlockquote(
	*NodeBlockquote,
) error

LeaveBlockquote is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveCode

func (BaseEnterLeaveVisitor) LeaveCode(
	*NodeCode,
) error

LeaveCode is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveCodeBlock

func (BaseEnterLeaveVisitor) LeaveCodeBlock(
	*NodeCodeBlock,
) error

LeaveCodeBlock is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveDocument

func (BaseEnterLeaveVisitor) LeaveDocument(
	*NodeDocument,
) error

LeaveDocument is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveEmphasis

func (BaseEnterLeaveVisitor) LeaveEmphasis(
	*NodeEmphasis,
) error

LeaveEmphasis is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveLink(
	*NodeLink,
) error

LeaveLink is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveLinkDef

func (BaseEnterLeaveVisitor) LeaveLinkDef(
	*NodeLinkDef,
) error

LeaveLinkDef is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveList

func (BaseEnterLeaveVisitor) LeaveList(
	*NodeList,
) error

LeaveList is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveListItem

func (BaseEnterLeaveVisitor) LeaveListItem(
	*NodeListItem,
) error

LeaveListItem is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveParagraph

func (BaseEnterLeaveVisitor) LeaveParagraph(
	*NodeParagraph,
) error

LeaveParagraph is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveRequirement

func (BaseEnterLeaveVisitor) LeaveRequirement(
	*NodeRequirement,
) error

LeaveRequirement is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveScenario

func (BaseEnterLeaveVisitor) LeaveScenario(
	*NodeScenario,
) error

LeaveScenario is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveSection

func (BaseEnterLeaveVisitor) LeaveSection(
	*NodeSection,
) error

LeaveSection is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveStrikethrough

func (BaseEnterLeaveVisitor) LeaveStrikethrough(
	*NodeStrikethrough,
) error

LeaveStrikethrough is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveStrong

func (BaseEnterLeaveVisitor) LeaveStrong(
	*NodeStrong,
) error

LeaveStrong is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveText

func (BaseEnterLeaveVisitor) LeaveText(
	*NodeText,
) error

LeaveText is a no-op that returns nil.

func (BaseEnterLeaveVisitor) LeaveWikilink(
	*NodeWikilink,
) error

LeaveWikilink is a no-op that returns nil.

type BaseTransformVisitor

type BaseTransformVisitor struct{}

BaseTransformVisitor provides default no-op implementations for all TransformVisitor methods. Each method returns (original, ActionKeep, nil) by default. Embed this struct in custom transform visitors to only override the methods you need.

func (BaseTransformVisitor) TransformBlockquote

func (BaseTransformVisitor) TransformBlockquote(
	n *NodeBlockquote,
) (Node, TransformAction, error)

TransformBlockquote returns the blockquote unchanged.

func (BaseTransformVisitor) TransformCode

func (BaseTransformVisitor) TransformCode(
	n *NodeCode,
) (Node, TransformAction, error)

TransformCode returns the code unchanged.

func (BaseTransformVisitor) TransformCodeBlock

func (BaseTransformVisitor) TransformCodeBlock(
	n *NodeCodeBlock,
) (Node, TransformAction, error)

TransformCodeBlock returns the code block unchanged.

func (BaseTransformVisitor) TransformDocument

func (BaseTransformVisitor) TransformDocument(
	n *NodeDocument,
) (Node, TransformAction, error)

TransformDocument returns the document unchanged.

func (BaseTransformVisitor) TransformEmphasis

func (BaseTransformVisitor) TransformEmphasis(
	n *NodeEmphasis,
) (Node, TransformAction, error)

TransformEmphasis returns the emphasis unchanged.

func (BaseTransformVisitor) TransformLink(
	n *NodeLink,
) (Node, TransformAction, error)

TransformLink returns the link unchanged.

func (BaseTransformVisitor) TransformLinkDef

func (BaseTransformVisitor) TransformLinkDef(
	n *NodeLinkDef,
) (Node, TransformAction, error)

TransformLinkDef returns the link definition unchanged.

func (BaseTransformVisitor) TransformList

func (BaseTransformVisitor) TransformList(
	n *NodeList,
) (Node, TransformAction, error)

TransformList returns the list unchanged.

func (BaseTransformVisitor) TransformListItem

func (BaseTransformVisitor) TransformListItem(
	n *NodeListItem,
) (Node, TransformAction, error)

TransformListItem returns the list item unchanged.

func (BaseTransformVisitor) TransformParagraph

func (BaseTransformVisitor) TransformParagraph(
	n *NodeParagraph,
) (Node, TransformAction, error)

TransformParagraph returns the paragraph unchanged.

func (BaseTransformVisitor) TransformRequirement

func (BaseTransformVisitor) TransformRequirement(
	n *NodeRequirement,
) (Node, TransformAction, error)

TransformRequirement returns the requirement unchanged.

func (BaseTransformVisitor) TransformScenario

func (BaseTransformVisitor) TransformScenario(
	n *NodeScenario,
) (Node, TransformAction, error)

TransformScenario returns the scenario unchanged.

func (BaseTransformVisitor) TransformSection

func (BaseTransformVisitor) TransformSection(
	n *NodeSection,
) (Node, TransformAction, error)

TransformSection returns the section unchanged.

func (BaseTransformVisitor) TransformStrikethrough

func (BaseTransformVisitor) TransformStrikethrough(
	n *NodeStrikethrough,
) (Node, TransformAction, error)

TransformStrikethrough returns the strikethrough unchanged.

func (BaseTransformVisitor) TransformStrong

func (BaseTransformVisitor) TransformStrong(
	n *NodeStrong,
) (Node, TransformAction, error)

TransformStrong returns the strong unchanged.

func (BaseTransformVisitor) TransformText

func (BaseTransformVisitor) TransformText(
	n *NodeText,
) (Node, TransformAction, error)

TransformText returns the text unchanged.

func (BaseTransformVisitor) TransformWikilink(
	n *NodeWikilink,
) (Node, TransformAction, error)

TransformWikilink returns the wikilink unchanged.

type BaseVisitor

type BaseVisitor struct{}

BaseVisitor provides no-op default implementations for all Visitor methods. Embed this struct in custom visitors to only override the methods you need.

func (BaseVisitor) VisitBlockquote

func (BaseVisitor) VisitBlockquote(
	*NodeBlockquote,
) error

VisitBlockquote is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitCode

func (BaseVisitor) VisitCode(
	*NodeCode,
) error

VisitCode is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitCodeBlock

func (BaseVisitor) VisitCodeBlock(
	*NodeCodeBlock,
) error

VisitCodeBlock is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitDocument

func (BaseVisitor) VisitDocument(
	*NodeDocument,
) error

VisitDocument is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitEmphasis

func (BaseVisitor) VisitEmphasis(
	*NodeEmphasis,
) error

VisitEmphasis is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitLink(
	*NodeLink,
) error

VisitLink is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitLinkDef

func (BaseVisitor) VisitLinkDef(
	*NodeLinkDef,
) error

VisitLinkDef is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitList

func (BaseVisitor) VisitList(
	*NodeList,
) error

VisitList is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitListItem

func (BaseVisitor) VisitListItem(
	*NodeListItem,
) error

VisitListItem is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitParagraph

func (BaseVisitor) VisitParagraph(
	*NodeParagraph,
) error

VisitParagraph is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitRequirement

func (BaseVisitor) VisitRequirement(
	*NodeRequirement,
) error

VisitRequirement is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitScenario

func (BaseVisitor) VisitScenario(
	*NodeScenario,
) error

VisitScenario is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitSection

func (BaseVisitor) VisitSection(
	*NodeSection,
) error

VisitSection is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitStrikethrough

func (BaseVisitor) VisitStrikethrough(
	*NodeStrikethrough,
) error

VisitStrikethrough is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitStrong

func (BaseVisitor) VisitStrong(
	*NodeStrong,
) error

VisitStrong is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitText

func (BaseVisitor) VisitText(
	*NodeText,
) error

VisitText is a no-op that returns nil (continue traversal).

func (BaseVisitor) VisitWikilink(
	*NodeWikilink,
) error

VisitWikilink is a no-op that returns nil (continue traversal).

type BuilderValidationError

type BuilderValidationError struct {
	Field   string
	Message string
	Index   int // For array fields, the index of the problematic element
}

BuilderValidationError represents a validation error in the builder.

func (*BuilderValidationError) Error

func (e *BuilderValidationError) Error() string

type ContextVisitor

type ContextVisitor interface {
	VisitDocumentWithContext(
		*NodeDocument,
		*VisitorContext,
	) error
	VisitSectionWithContext(
		*NodeSection,
		*VisitorContext,
	) error
	VisitRequirementWithContext(
		*NodeRequirement,
		*VisitorContext,
	) error
	VisitScenarioWithContext(
		*NodeScenario,
		*VisitorContext,
	) error
	VisitParagraphWithContext(
		*NodeParagraph,
		*VisitorContext,
	) error
	VisitListWithContext(
		*NodeList,
		*VisitorContext,
	) error
	VisitListItemWithContext(
		*NodeListItem,
		*VisitorContext,
	) error
	VisitCodeBlockWithContext(
		*NodeCodeBlock,
		*VisitorContext,
	) error
	VisitBlockquoteWithContext(
		*NodeBlockquote,
		*VisitorContext,
	) error
	VisitTextWithContext(
		*NodeText,
		*VisitorContext,
	) error
	VisitStrongWithContext(
		*NodeStrong,
		*VisitorContext,
	) error
	VisitEmphasisWithContext(
		*NodeEmphasis,
		*VisitorContext,
	) error
	VisitStrikethroughWithContext(
		*NodeStrikethrough,
		*VisitorContext,
	) error
	VisitCodeWithContext(
		*NodeCode,
		*VisitorContext,
	) error
	VisitLinkWithContext(
		*NodeLink,
		*VisitorContext,
	) error
	VisitLinkDefWithContext(
		*NodeLinkDef,
		*VisitorContext,
	) error
	VisitWikilinkWithContext(
		*NodeWikilink,
		*VisitorContext,
	) error
}

ContextVisitor is a visitor interface that receives context information during traversal, including parent node access.

type Delta

type Delta struct {
	// Root is the document node containing the entire AST.
	Root Node

	// Added contains requirements that are being added.
	Added []*Requirement

	// Modified contains requirements that are being modified.
	Modified []*Requirement

	// Removed contains the names of requirements being removed.
	// Only names are stored since the requirements no longer exist.
	Removed []string

	// Renamed contains rename operations with from/to pairs.
	Renamed []*RenamedRequirement

	// Errors contains all parse errors encountered during parsing.
	Errors []ParseError
}

Delta represents a parsed delta specification file. Delta files describe changes to requirements: additions, modifications, removals, and renames.

type DeltaType

type DeltaType string

DeltaType represents the type of change in a delta specification.

const (
	DeltaAdded    DeltaType = "ADDED"
	DeltaModified DeltaType = "MODIFIED"
	DeltaRemoved  DeltaType = "REMOVED"
	DeltaRenamed  DeltaType = "RENAMED"
)

Delta type constants for categorizing requirement changes.

type EditRegion

type EditRegion struct {
	StartOffset  int // Where edit begins (same in both old and new)
	OldEndOffset int // Where old content ended (exclusive)
	NewEndOffset int // Where new content ends (exclusive)
}

EditRegion describes a contiguous edit between old and new source. It represents the region where content differs between the two versions.

func (EditRegion) Delta

func (e EditRegion) Delta() int

Delta returns the byte offset change caused by this edit. Positive means content was added, negative means content was removed.

func (EditRegion) IsDelete

func (e EditRegion) IsDelete() bool

IsDelete returns true if this edit is a pure deletion (no content added).

func (EditRegion) IsInsert

func (e EditRegion) IsInsert() bool

IsInsert returns true if this edit is a pure insertion (no content removed).

func (EditRegion) IsReplace

func (e EditRegion) IsReplace() bool

IsReplace returns true if this edit replaces content (both removes and adds).

func (EditRegion) NewLength

func (e EditRegion) NewLength() int

NewLength returns the number of bytes added in the new source.

func (EditRegion) OldLength

func (e EditRegion) OldLength() int

OldLength returns the number of bytes removed from the old source.

type EnterLeaveVisitor

type EnterLeaveVisitor interface {
	EnterDocument(*NodeDocument) error
	LeaveDocument(*NodeDocument) error
	EnterSection(*NodeSection) error
	LeaveSection(*NodeSection) error
	EnterRequirement(*NodeRequirement) error
	LeaveRequirement(*NodeRequirement) error
	EnterScenario(*NodeScenario) error
	LeaveScenario(*NodeScenario) error
	EnterParagraph(*NodeParagraph) error
	LeaveParagraph(*NodeParagraph) error
	EnterList(*NodeList) error
	LeaveList(*NodeList) error
	EnterListItem(*NodeListItem) error
	LeaveListItem(*NodeListItem) error
	EnterCodeBlock(*NodeCodeBlock) error
	LeaveCodeBlock(*NodeCodeBlock) error
	EnterBlockquote(*NodeBlockquote) error
	LeaveBlockquote(*NodeBlockquote) error
	EnterText(*NodeText) error
	LeaveText(*NodeText) error
	EnterStrong(*NodeStrong) error
	LeaveStrong(*NodeStrong) error
	EnterEmphasis(*NodeEmphasis) error
	LeaveEmphasis(*NodeEmphasis) error
	EnterStrikethrough(*NodeStrikethrough) error
	LeaveStrikethrough(*NodeStrikethrough) error
	EnterCode(*NodeCode) error
	LeaveCode(*NodeCode) error
	EnterLink(*NodeLink) error
	LeaveLink(*NodeLink) error
	EnterLinkDef(*NodeLinkDef) error
	LeaveLinkDef(*NodeLinkDef) error
	EnterWikilink(*NodeWikilink) error
	LeaveWikilink(*NodeWikilink) error
}

EnterLeaveVisitor defines the interface for visitors that need to perform actions both before (Enter) and after (Leave) visiting a node's children. This is useful for operations that need to maintain state or perform cleanup, such as building output or managing a stack.

type FlexibleTaskMatch added in v0.0.9

type FlexibleTaskMatch struct {
	Number  string // Explicit number if present (e.g., "1.1", "1.", "1"), empty otherwise
	Status  rune   // ' ' for unchecked, 'x' or 'X' for checked
	Content string // The task description
}

FlexibleTaskMatch holds the parsed result of any task checkbox line. Unlike NumberedTaskMatch, this accepts tasks with or without numbers.

func MatchFlexibleTask added in v0.0.9

func MatchFlexibleTask(
	line string,
) (*FlexibleTaskMatch, bool)

MatchFlexibleTask parses any task checkbox line from tasks.md format. Accepts all formats:

  • "- [ ] 1.1 Task description" (decimal)
  • "- [ ] 1. Task description" (simple dot)
  • "- [ ] 1 Task description" (number only)
  • "- [ ] Task description" (no number)

Returns the parsed task match and true if matched, or nil and false.

type IncrementalParseState

type IncrementalParseState struct {
	// LinkDefs contains collected link definitions from the document.
	// These can be reused if the link definition section hasn't changed.
	LinkDefs map[string]linkDefinition

	// LineIndex is the cached line index for position calculations.
	// May be partially reusable if only part of the document changed.
	LineIndex *LineIndex

	// RootHash is the content hash of the root document node.
	// Used to detect if the document has changed.
	RootHash uint64
}

IncrementalParseState holds state that can be reused across incremental parses. This includes link definitions, line index data, and cached information.

func NewIncrementalParseState

func NewIncrementalParseState(
	tree Node,
	source []byte,
) *IncrementalParseState

NewIncrementalParseState creates a new incremental parse state from a parsed tree.

func (*IncrementalParseState) CanReuseLinkDefs

func (s *IncrementalParseState) CanReuseLinkDefs(
	edit EditRegion,
	oldTree Node,
) bool

CanReuseLinkDefs determines if link definitions can be reused from old state. Link definitions can be reused if the edit region doesn't overlap with any link definition in the document.

func (*IncrementalParseState) UpdateLineIndex

func (s *IncrementalParseState) UpdateLineIndex(
	edit EditRegion,
	oldSource, newSource []byte,
)

UpdateLineIndex updates the line index for an edit. If the edit only affects content within a single line (no newlines added/removed), the existing line index can be adjusted rather than rebuilt from scratch.

type LexError

type LexError struct {
	Offset  int    // Byte offset where error occurred
	Message string // Error description
}

LexError represents an error encountered during lexing. It captures position and message from TokenError tokens.

func (LexError) Error

func (e LexError) Error() string

Error implements the error interface.

type LexerState

type LexerState uint8

LexerState represents the current context of the lexer. Different states affect how characters are tokenized.

const (
	// StateNormal is the default state for normal markdown content.
	StateNormal LexerState = iota
	// StateFencedCode is active inside a fenced code block (“` or ~~~).
	// In this state, only TokenText and TokenNewline are emitted.
	StateFencedCode
	// StateInlineCode is active inside inline code (backticks).
	// Content is emitted as TokenText until matching backticks.
	StateInlineCode
	// StateLinkURL is active inside a link URL parentheses.
	// Special characters are treated as TokenText.
	StateLinkURL
)

type LineIndex

type LineIndex struct {
	// contains filtered or unexported fields
}

LineIndex provides efficient conversion from byte offsets to line/column positions. It uses lazy construction - the line index is only built on the first query. The index supports O(log n) lookups via binary search.

func NewLineIndex

func NewLineIndex(source []byte) *LineIndex

NewLineIndex creates a new LineIndex for the given source. The index is lazily constructed on the first query.

func (*LineIndex) LineCol

func (idx *LineIndex) LineCol(
	offset int,
) (line, col int)

LineCol returns the 1-based line number and 0-based column for a byte offset. If the offset is beyond the source length, returns the position at end of source. If the offset is negative, returns line 1, column 0.

func (*LineIndex) LineCount

func (idx *LineIndex) LineCount() int

LineCount returns the total number of lines in the source. This triggers index construction if not already built.

func (*LineIndex) LineEnd

func (idx *LineIndex) LineEnd(lineNum int) int

LineEnd returns the byte offset of the end of the given 1-based line number. The end is the offset of the newline character (or end of source for last line). Returns 0 for line <= 0 or if source is empty.

func (*LineIndex) LineStart

func (idx *LineIndex) LineStart(lineNum int) int

LineStart returns the byte offset of the start of the given 1-based line number. Returns 0 for line <= 0 or if source is empty. Returns the start of the last line if lineNum exceeds the total line count.

func (*LineIndex) OffsetAt

func (idx *LineIndex) OffsetAt(
	line, col int,
) int

OffsetAt converts a 1-based line and 0-based column to a byte offset. Returns -1 if the line number is invalid. Clamps the column to the line's length if it exceeds it.

func (*LineIndex) PositionAt

func (idx *LineIndex) PositionAt(
	offset int,
) Position

PositionAt returns a Position struct for the given byte offset. This combines the line, column, and original offset into a single struct.

type Named

type Named interface {
	// Name returns the name of the node.
	Name() string
}

Named is an interface for nodes that have a Name() method. This includes NodeRequirement and NodeScenario.

type Node

type Node interface {
	// NodeType returns the type classification of this node.
	NodeType() NodeType

	// Span returns the byte offset range (start, end) of this node.
	// Start is inclusive, end is exclusive.
	Span() (start, end int)

	// Hash returns a content hash for identity tracking and caching.
	// Nodes with the same hash have the same semantic content.
	Hash() uint64

	// Source returns a zero-copy byte slice view into the original source.
	// This remains valid as long as the original source buffer is retained.
	Source() []byte

	// Children returns an immutable copy of child nodes.
	// Modifications to the returned slice do not affect the node.
	Children() []Node

	// Equal performs deep structural comparison with another node.
	Equal(other Node) bool
}

Node is the interface implemented by all AST nodes. All nodes are immutable after creation - modifications require creating new nodes via builders.

func Find

func Find(root Node, pred Predicate) []Node

Find returns all nodes in the AST where pred returns true. Results are in pre-order traversal order (depth-first). Returns an empty slice (not nil) if no nodes match.

func FindFirst

func FindFirst(root Node, pred Predicate) Node

FindFirst returns the first node where pred returns true, or nil if none. Traversal stops immediately upon finding a match (short-circuit).

func GetChildren

func GetChildren(capacity int) []Node

GetChildren retrieves a children slice from the appropriate pool based on capacity. The returned slice has length 0 and capacity >= the requested capacity.

func NodeAtOffset

func NodeAtOffset(root Node, offset int) Node

NodeAtOffset finds the innermost node containing the given byte offset. This is useful for editor integrations to find what the cursor is on.

func NodesAtOffset

func NodesAtOffset(root Node, offset int) []Node

NodesAtOffset finds all nodes containing the given byte offset, from outermost to innermost.

func Transform

func Transform(
	root Node,
	v TransformVisitor,
) (Node, error)

Transform applies a TransformVisitor to an AST using post-order traversal. Children are transformed before their parent, so parent transform methods see the results of child transformations.

The original AST is not modified; Transform returns a new tree with the transformations applied.

If the root node is deleted (ActionDelete returned for root), Transform returns (nil, nil).

If any transform method returns a non-nil error, Transform stops immediately and returns the error. Partial results are not returned.

type NodeBlockquote

type NodeBlockquote struct {
	// contains filtered or unexported fields
}

NodeBlockquote represents blockquoted content (lines starting with >).

func GetBlockquote

func GetBlockquote() *NodeBlockquote

GetBlockquote retrieves a NodeBlockquote from the pool.

func (*NodeBlockquote) Children

func (n *NodeBlockquote) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeBlockquote) Equal

func (n *NodeBlockquote) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeBlockquote) Hash

func (n *NodeBlockquote) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeBlockquote) NodeType

func (n *NodeBlockquote) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeBlockquote) Source

func (n *NodeBlockquote) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeBlockquote) Span

func (n *NodeBlockquote) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeBlockquote) ToBuilder

func (n *NodeBlockquote) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeBuilder

type NodeBuilder struct {
	// contains filtered or unexported fields
}

NodeBuilder provides a fluent API for constructing AST nodes. It supports building new nodes and transforming existing ones.

func NewNodeBuilder

func NewNodeBuilder(
	nodeType NodeType,
) *NodeBuilder

NewNodeBuilder creates a new builder for the specified node type.

func (*NodeBuilder) Build

func (b *NodeBuilder) Build() Node

Build creates the immutable node from the builder state. It validates the builder and computes the content hash. Returns nil if validation fails (caller should check Validate() first for error details).

func (*NodeBuilder) Validate

func (b *NodeBuilder) Validate() error

Validate checks that the builder state is valid. Returns an error if validation fails.

func (*NodeBuilder) WithAnchor

func (b *NodeBuilder) WithAnchor(
	anchor []byte,
) *NodeBuilder

WithAnchor sets the anchor (for Wikilink nodes).

func (*NodeBuilder) WithChecked

func (b *NodeBuilder) WithChecked(
	checked *bool,
) *NodeBuilder

WithChecked sets the checkbox state (for ListItem nodes). Pass nil for no checkbox, pointer to bool for checkbox state.

func (*NodeBuilder) WithChildren

func (b *NodeBuilder) WithChildren(
	children []Node,
) *NodeBuilder

WithChildren sets the children nodes.

func (*NodeBuilder) WithContent

func (b *NodeBuilder) WithContent(
	content []byte,
) *NodeBuilder

WithContent sets the code content (for CodeBlock nodes).

func (*NodeBuilder) WithDeltaType

func (b *NodeBuilder) WithDeltaType(
	deltaType string,
) *NodeBuilder

WithDeltaType sets the delta type (for Section nodes).

func (*NodeBuilder) WithDisplay

func (b *NodeBuilder) WithDisplay(
	display []byte,
) *NodeBuilder

WithDisplay sets the display text (for Wikilink nodes).

func (*NodeBuilder) WithEnd

func (b *NodeBuilder) WithEnd(
	end int,
) *NodeBuilder

WithEnd sets the end byte offset.

func (*NodeBuilder) WithKeyword

func (b *NodeBuilder) WithKeyword(
	keyword string,
) *NodeBuilder

WithKeyword sets the keyword (for ListItem nodes).

func (*NodeBuilder) WithLanguage

func (b *NodeBuilder) WithLanguage(
	language []byte,
) *NodeBuilder

WithLanguage sets the language (for CodeBlock nodes).

func (*NodeBuilder) WithLevel

func (b *NodeBuilder) WithLevel(
	level int,
) *NodeBuilder

WithLevel sets the header level (for Section nodes).

func (*NodeBuilder) WithLinkTitle

func (b *NodeBuilder) WithLinkTitle(
	title []byte,
) *NodeBuilder

WithLinkTitle sets the link title (for Link nodes).

func (*NodeBuilder) WithName

func (b *NodeBuilder) WithName(
	name string,
) *NodeBuilder

WithName sets the name (for Requirement and Scenario nodes).

func (*NodeBuilder) WithOrdered

func (b *NodeBuilder) WithOrdered(
	ordered bool,
) *NodeBuilder

WithOrdered sets whether the list is ordered (for List nodes).

func (*NodeBuilder) WithSource

func (b *NodeBuilder) WithSource(
	source []byte,
) *NodeBuilder

WithSource sets the source byte slice.

func (*NodeBuilder) WithStart

func (b *NodeBuilder) WithStart(
	start int,
) *NodeBuilder

WithStart sets the start byte offset.

func (*NodeBuilder) WithTarget

func (b *NodeBuilder) WithTarget(
	target []byte,
) *NodeBuilder

WithTarget sets the target (for Wikilink nodes).

func (*NodeBuilder) WithTitle

func (b *NodeBuilder) WithTitle(
	title []byte,
) *NodeBuilder

WithTitle sets the title (for Section nodes).

func (*NodeBuilder) WithURL

func (b *NodeBuilder) WithURL(
	url []byte,
) *NodeBuilder

WithURL sets the URL (for Link nodes).

type NodeCode

type NodeCode struct {
	// contains filtered or unexported fields
}

NodeCode represents inline code (`code`).

func GetCode

func GetCode() *NodeCode

GetCode retrieves a NodeCode from the pool.

func (*NodeCode) Children

func (n *NodeCode) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeCode) Code

func (n *NodeCode) Code() string

Code returns the code content as a string. This creates a copy; use Source() for zero-copy access.

func (*NodeCode) Equal

func (n *NodeCode) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeCode) Hash

func (n *NodeCode) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeCode) NodeType

func (n *NodeCode) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeCode) Source

func (n *NodeCode) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeCode) Span

func (n *NodeCode) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeCode) ToBuilder

func (n *NodeCode) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeCodeBlock

type NodeCodeBlock struct {
	// contains filtered or unexported fields
}

NodeCodeBlock represents a fenced code block (``` or ~~~).

func GetCodeBlock

func GetCodeBlock() *NodeCodeBlock

GetCodeBlock retrieves a NodeCodeBlock from the pool.

func (*NodeCodeBlock) Children

func (n *NodeCodeBlock) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeCodeBlock) Content

func (n *NodeCodeBlock) Content() []byte

Content returns the code content (without fence lines) as a byte slice.

func (*NodeCodeBlock) Equal

func (n *NodeCodeBlock) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeCodeBlock) Hash

func (n *NodeCodeBlock) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeCodeBlock) Language

func (n *NodeCodeBlock) Language() []byte

Language returns the language identifier as a byte slice. Returns nil if no language was specified.

func (*NodeCodeBlock) NodeType

func (n *NodeCodeBlock) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeCodeBlock) Source

func (n *NodeCodeBlock) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeCodeBlock) Span

func (n *NodeCodeBlock) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeCodeBlock) ToBuilder

func (n *NodeCodeBlock) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeDocument

type NodeDocument struct {
	// contains filtered or unexported fields
}

NodeDocument is the root node of an AST. It contains all top-level block nodes from the parsed document.

func GetDocument

func GetDocument() *NodeDocument

GetDocument retrieves a NodeDocument from the pool.

func (*NodeDocument) Children

func (n *NodeDocument) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeDocument) Equal

func (n *NodeDocument) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeDocument) Hash

func (n *NodeDocument) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeDocument) NodeType

func (n *NodeDocument) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeDocument) Source

func (n *NodeDocument) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeDocument) Span

func (n *NodeDocument) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeDocument) ToBuilder

func (n *NodeDocument) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeEmphasis

type NodeEmphasis struct {
	// contains filtered or unexported fields
}

NodeEmphasis represents italic emphasis (*text* or _text_).

func GetEmphasis

func GetEmphasis() *NodeEmphasis

GetEmphasis retrieves a NodeEmphasis from the pool.

func (*NodeEmphasis) Children

func (n *NodeEmphasis) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeEmphasis) Equal

func (n *NodeEmphasis) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeEmphasis) Hash

func (n *NodeEmphasis) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeEmphasis) NodeType

func (n *NodeEmphasis) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeEmphasis) Source

func (n *NodeEmphasis) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeEmphasis) Span

func (n *NodeEmphasis) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeEmphasis) ToBuilder

func (n *NodeEmphasis) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeLink struct {
	// contains filtered or unexported fields
}

NodeLink represents a link [text](url "title") or [text][ref].

func GetLink() *NodeLink

GetLink retrieves a NodeLink from the pool.

func (*NodeLink) Children

func (n *NodeLink) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeLink) Equal

func (n *NodeLink) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeLink) Hash

func (n *NodeLink) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeLink) NodeType

func (n *NodeLink) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeLink) Source

func (n *NodeLink) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeLink) Span

func (n *NodeLink) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeLink) Title

func (n *NodeLink) Title() []byte

Title returns the optional link title as a byte slice. Returns nil if no title was specified.

func (*NodeLink) ToBuilder

func (n *NodeLink) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

func (*NodeLink) URL

func (n *NodeLink) URL() []byte

URL returns the link destination as a byte slice.

type NodeLinkDef

type NodeLinkDef struct {
	// contains filtered or unexported fields
}

NodeLinkDef represents a link definition [ref]: url "title". Link definitions are collected during parsing and used to resolve reference links.

func GetLinkDef

func GetLinkDef() *NodeLinkDef

GetLinkDef retrieves a NodeLinkDef from the pool.

func (*NodeLinkDef) Children

func (n *NodeLinkDef) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeLinkDef) Equal

func (n *NodeLinkDef) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeLinkDef) Hash

func (n *NodeLinkDef) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeLinkDef) NodeType

func (n *NodeLinkDef) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeLinkDef) Source

func (n *NodeLinkDef) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeLinkDef) Span

func (n *NodeLinkDef) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeLinkDef) Title

func (n *NodeLinkDef) Title() []byte

Title returns the optional link title as a byte slice. Returns nil if no title was specified.

func (*NodeLinkDef) ToBuilder

func (n *NodeLinkDef) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

func (*NodeLinkDef) URL

func (n *NodeLinkDef) URL() []byte

URL returns the link destination as a byte slice.

type NodeList

type NodeList struct {
	// contains filtered or unexported fields
}

NodeList represents an unordered or ordered list. Its children are NodeListItem nodes.

func GetList

func GetList() *NodeList

GetList retrieves a NodeList from the pool.

func (*NodeList) Children

func (n *NodeList) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeList) Equal

func (n *NodeList) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeList) Hash

func (n *NodeList) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeList) NodeType

func (n *NodeList) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeList) Ordered

func (n *NodeList) Ordered() bool

Ordered returns true if this is an ordered (numbered) list.

func (*NodeList) Source

func (n *NodeList) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeList) Span

func (n *NodeList) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeList) ToBuilder

func (n *NodeList) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeListItem

type NodeListItem struct {
	// contains filtered or unexported fields
}

NodeListItem represents a single list item. It may be a task item with a checkbox, or a Spectr bullet with WHEN/THEN/AND keyword.

func GetListItem

func GetListItem() *NodeListItem

GetListItem retrieves a NodeListItem from the pool.

func (*NodeListItem) Checked

func (n *NodeListItem) Checked() (isChecked, hasCheckbox bool)

Checked returns the checkbox state and whether a checkbox is present. If no checkbox, returns (false, false). If unchecked checkbox, returns (false, true). If checked checkbox, returns (true, true).

func (*NodeListItem) Children

func (n *NodeListItem) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeListItem) Equal

func (n *NodeListItem) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeListItem) Hash

func (n *NodeListItem) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeListItem) Keyword

func (n *NodeListItem) Keyword() string

Keyword returns the Spectr bullet keyword (WHEN, THEN, AND) or empty string.

func (*NodeListItem) NodeType

func (n *NodeListItem) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeListItem) Source

func (n *NodeListItem) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeListItem) Span

func (n *NodeListItem) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeListItem) ToBuilder

func (n *NodeListItem) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeParagraph

type NodeParagraph struct {
	// contains filtered or unexported fields
}

NodeParagraph represents a paragraph of text. Its children are inline nodes (text, emphasis, links, etc.).

func GetParagraph

func GetParagraph() *NodeParagraph

GetParagraph retrieves a NodeParagraph from the pool.

func (*NodeParagraph) Children

func (n *NodeParagraph) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeParagraph) Equal

func (n *NodeParagraph) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeParagraph) Hash

func (n *NodeParagraph) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeParagraph) NodeType

func (n *NodeParagraph) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeParagraph) Source

func (n *NodeParagraph) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeParagraph) Span

func (n *NodeParagraph) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeParagraph) ToBuilder

func (n *NodeParagraph) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeRequirement

type NodeRequirement struct {
	// contains filtered or unexported fields
}

NodeRequirement represents a Spectr requirement header (### Requirement: Name).

func GetRequirement

func GetRequirement() *NodeRequirement

GetRequirement retrieves a NodeRequirement from the pool.

func (*NodeRequirement) Children

func (n *NodeRequirement) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeRequirement) Equal

func (n *NodeRequirement) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeRequirement) Hash

func (n *NodeRequirement) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeRequirement) Name

func (n *NodeRequirement) Name() string

Name returns the requirement name extracted from the header.

func (*NodeRequirement) NodeType

func (n *NodeRequirement) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeRequirement) Source

func (n *NodeRequirement) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeRequirement) Span

func (n *NodeRequirement) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeRequirement) ToBuilder

func (n *NodeRequirement) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeScenario

type NodeScenario struct {
	// contains filtered or unexported fields
}

NodeScenario represents a Spectr scenario header (#### Scenario: Name).

func GetScenario

func GetScenario() *NodeScenario

GetScenario retrieves a NodeScenario from the pool.

func (*NodeScenario) Children

func (n *NodeScenario) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeScenario) Equal

func (n *NodeScenario) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeScenario) Hash

func (n *NodeScenario) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeScenario) Name

func (n *NodeScenario) Name() string

Name returns the scenario name extracted from the header.

func (*NodeScenario) NodeType

func (n *NodeScenario) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeScenario) Source

func (n *NodeScenario) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeScenario) Span

func (n *NodeScenario) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeScenario) ToBuilder

func (n *NodeScenario) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeSection

type NodeSection struct {
	// contains filtered or unexported fields
}

NodeSection represents an ATX-style header (H1-H6) and its content. For Spectr delta files, it may have a DeltaType indicating the change type.

func GetSection

func GetSection() *NodeSection

GetSection retrieves a NodeSection from the pool.

func (*NodeSection) Children

func (n *NodeSection) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeSection) DeltaType

func (n *NodeSection) DeltaType() string

DeltaType returns the delta type for Spectr delta sections. Returns one of "ADDED", "MODIFIED", "REMOVED", "RENAMED", or empty string.

func (*NodeSection) Equal

func (n *NodeSection) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeSection) Hash

func (n *NodeSection) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeSection) Level

func (n *NodeSection) Level() int

Level returns the header level (1-6).

func (*NodeSection) NodeType

func (n *NodeSection) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeSection) Source

func (n *NodeSection) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeSection) Span

func (n *NodeSection) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeSection) Title

func (n *NodeSection) Title() []byte

Title returns the header text as a byte slice.

func (*NodeSection) ToBuilder

func (n *NodeSection) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeStrikethrough

type NodeStrikethrough struct {
	// contains filtered or unexported fields
}

NodeStrikethrough represents struck text (~~text~~).

func GetStrikethrough

func GetStrikethrough() *NodeStrikethrough

GetStrikethrough retrieves a NodeStrikethrough from the pool.

func (*NodeStrikethrough) Children

func (n *NodeStrikethrough) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeStrikethrough) Equal

func (n *NodeStrikethrough) Equal(
	other Node,
) bool

Equal performs deep structural comparison with another node.

func (*NodeStrikethrough) Hash

func (n *NodeStrikethrough) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeStrikethrough) NodeType

func (n *NodeStrikethrough) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeStrikethrough) Source

func (n *NodeStrikethrough) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeStrikethrough) Span

func (n *NodeStrikethrough) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeStrikethrough) ToBuilder

func (n *NodeStrikethrough) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeStrong

type NodeStrong struct {
	// contains filtered or unexported fields
}

NodeStrong represents bold/strong emphasis (**text** or __text__).

func GetStrong

func GetStrong() *NodeStrong

GetStrong retrieves a NodeStrong from the pool.

func (*NodeStrong) Children

func (n *NodeStrong) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeStrong) Equal

func (n *NodeStrong) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeStrong) Hash

func (n *NodeStrong) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeStrong) NodeType

func (n *NodeStrong) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeStrong) Source

func (n *NodeStrong) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeStrong) Span

func (n *NodeStrong) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeStrong) ToBuilder

func (n *NodeStrong) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeText

type NodeText struct {
	// contains filtered or unexported fields
}

NodeText represents plain text content.

func GetText

func GetText() *NodeText

GetText retrieves a NodeText from the pool.

func (*NodeText) Children

func (n *NodeText) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeText) Equal

func (n *NodeText) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeText) Hash

func (n *NodeText) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeText) NodeType

func (n *NodeText) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeText) Source

func (n *NodeText) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeText) Span

func (n *NodeText) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeText) Text

func (n *NodeText) Text() string

Text returns the text content as a string. This creates a copy; use Source() for zero-copy access.

func (*NodeText) ToBuilder

func (n *NodeText) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NodeType

type NodeType uint8

NodeType represents the type of an AST node. Each type corresponds to a different markdown construct.

const (

	// NodeTypeDocument is the root node containing all top-level nodes.
	NodeTypeDocument NodeType = iota
	// NodeTypeSection represents an H2+ header and its content.
	NodeTypeSection
	// NodeTypeRequirement represents a ### Requirement: header.
	NodeTypeRequirement
	// NodeTypeScenario represents a #### Scenario: header.
	NodeTypeScenario
	// NodeTypeParagraph represents paragraph content.
	NodeTypeParagraph
	// NodeTypeList represents an unordered or ordered list.
	NodeTypeList
	// NodeTypeListItem represents a single list item.
	NodeTypeListItem
	// NodeTypeCodeBlock represents a fenced code block.
	NodeTypeCodeBlock
	// NodeTypeBlockquote represents blockquoted content.
	NodeTypeBlockquote

	// NodeTypeText represents plain text content.
	NodeTypeText
	// NodeTypeStrong represents bold/strong emphasis (**text** or __text__).
	NodeTypeStrong
	// NodeTypeEmphasis represents italic emphasis (*text* or _text_).
	NodeTypeEmphasis
	// NodeTypeStrikethrough represents struck text (~~text~~).
	NodeTypeStrikethrough
	// NodeTypeCode represents inline code (`code`).
	NodeTypeCode
	// NodeTypeLink represents a link [text](url) or [text][ref].
	NodeTypeLink
	// NodeTypeLinkDef represents a link definition [ref]: url.
	NodeTypeLinkDef
	// NodeTypeWikilink represents a wikilink [[target|display#anchor]].
	NodeTypeWikilink
)

func (NodeType) String

func (t NodeType) String() string

String returns a human-readable name for the node type.

type NodeTypeStats

type NodeTypeStats struct {
	Type NodeType
	Gets uint64
	Puts uint64
}

NodeTypeStats contains get/put counts for a specific node type.

func GetNodeStats

func GetNodeStats() []NodeTypeStats

GetNodeStats returns statistics for all node types.

type NodeWikilink struct {
	// contains filtered or unexported fields
}

NodeWikilink represents a wikilink [[target|display#anchor]].

func GetWikilink() *NodeWikilink

GetWikilink retrieves a NodeWikilink from the pool.

func (*NodeWikilink) Anchor

func (n *NodeWikilink) Anchor() []byte

Anchor returns the optional anchor as a byte slice. Returns nil if no anchor was specified.

func (*NodeWikilink) Children

func (n *NodeWikilink) Children() []Node

Children returns an immutable copy of child nodes.

func (*NodeWikilink) Display

func (n *NodeWikilink) Display() []byte

Display returns the optional display text as a byte slice. Returns nil if no display text was specified (defaults to target).

func (*NodeWikilink) Equal

func (n *NodeWikilink) Equal(other Node) bool

Equal performs deep structural comparison with another node.

func (*NodeWikilink) Hash

func (n *NodeWikilink) Hash() uint64

Hash returns the content hash for identity tracking and caching.

func (*NodeWikilink) NodeType

func (n *NodeWikilink) NodeType() NodeType

NodeType returns the type classification of this node.

func (*NodeWikilink) Source

func (n *NodeWikilink) Source() []byte

Source returns a zero-copy byte slice view into the original source.

func (*NodeWikilink) Span

func (n *NodeWikilink) Span() (start, end int)

Span returns the byte offset range (start, end) of this node.

func (*NodeWikilink) Target

func (n *NodeWikilink) Target() []byte

Target returns the link target as a byte slice.

func (*NodeWikilink) ToBuilder

func (n *NodeWikilink) ToBuilder() *NodeBuilder

ToBuilder creates a builder pre-populated with this node's data.

type NumberedTaskMatch

type NumberedTaskMatch struct {
	Section string // The section number (e.g., "1")
	Number  string // The task number (e.g., "1.1")
	Status  rune   // ' ' for unchecked, 'x' or 'X' for checked
	Content string // The task description
}

NumberedTaskMatch holds the parsed result of a numbered task line.

func MatchNumberedTask

func MatchNumberedTask(
	line string,
) (*NumberedTaskMatch, bool)

MatchNumberedTask parses a numbered task line from tasks.md format. Format: "- [ ] 1.1 Task description" or "- [x] 2.3 Another task" Also accepts simpler format: "- [ ] 1. Task description" (no digits after dot) Returns the parsed task match and true if matched, or nil and false.

Example:

match, ok := MatchNumberedTask("- [ ] 1.1 Create the parser")
// match.Number = "1.1", match.Status = ' ', match.Content = "Create the parser"

match, ok := MatchNumberedTask("- [ ] 1. Simple task")
// match.Number = "1.", match.Status = ' ', match.Content = "Simple task"

type ParseError

type ParseError struct {
	Offset   int         // Byte offset where error occurred
	Message  string      // Human-readable error description
	Expected []TokenType // What tokens would have been valid (may be nil)
}

ParseError represents an error encountered during parsing. It contains the byte offset where the error occurred, a human-readable message, and optionally a list of expected token types.

func (ParseError) Error

func (e ParseError) Error() string

Error implements the error interface.

func (ParseError) Position

func (e ParseError) Position(
	idx *LineIndex,
) Position

Position converts the byte offset to a Position using the provided LineIndex.

type PoolStats

type PoolStats struct {
	TokenGets    uint64
	TokenPuts    uint64
	ChildrenGets uint64
	ChildrenPuts uint64
}

PoolStats contains statistics about pool usage. All fields are accessed atomically when stats are enabled.

func GetPoolStats

func GetPoolStats() PoolStats

GetPoolStats returns current pool statistics. Statistics are only collected when enabled via EnablePoolStats.

type Position

type Position struct {
	Line   int // 1-based line number
	Column int // 0-based byte offset within line
	Offset int // Original byte offset in source
}

Position represents a location in the source as line/column coordinates. Line numbers are 1-based (line 1 is the first line). Column numbers are 0-based byte offsets from the start of the line.

type PositionIndex

type PositionIndex struct {
	// contains filtered or unexported fields
}

PositionIndex provides efficient O(log n) queries for finding AST nodes at a given source position. It uses a sorted interval structure internally and is built lazily on the first query.

The index becomes stale when the AST is modified. Use Rebuild() to update the index with a new AST root, or create a new index.

func NewPositionIndex

func NewPositionIndex(
	root Node,
	source []byte,
) *PositionIndex

NewPositionIndex creates a new PositionIndex for the given AST root. The index is built lazily on the first query. If root is nil, the index will be empty and queries return nil/empty.

func (*PositionIndex) EnclosingRequirement

func (pi *PositionIndex) EnclosingRequirement(
	offset int,
) *NodeRequirement

EnclosingRequirement returns the NodeRequirement containing the given offset. Returns nil if the offset is not within any requirement.

This finds the innermost requirement if requirements are nested.

func (*PositionIndex) EnclosingScenario

func (pi *PositionIndex) EnclosingScenario(
	offset int,
) *NodeScenario

EnclosingScenario returns the NodeScenario containing the given offset. Returns nil if the offset is not within any scenario.

func (*PositionIndex) EnclosingSection

func (pi *PositionIndex) EnclosingSection(
	offset int,
) *NodeSection

EnclosingSection returns the NodeSection containing the given offset. Returns nil if the offset is not within any section.

This finds the innermost section if sections are nested.

func (*PositionIndex) IsStale

func (pi *PositionIndex) IsStale(
	root Node,
) bool

IsStale returns true if the index may be stale due to AST modifications. This checks if the root node's hash has changed since the index was created.

func (*PositionIndex) LineIndex

func (pi *PositionIndex) LineIndex() *LineIndex

LineIndex returns the integrated LineIndex for line/column calculations. Returns nil if no source was provided.

func (*PositionIndex) NodeAt

func (pi *PositionIndex) NodeAt(
	offset int,
) Node

NodeAt returns the innermost (most specific) node at the given offset. Returns nil if the offset is outside all nodes or if the AST is empty.

Query time is O(log n + k) where k is the number of nodes at that position.

func (*PositionIndex) NodeCount

func (pi *PositionIndex) NodeCount() int

NodeCount returns the total number of nodes in the index. This triggers index building if not already built.

func (*PositionIndex) NodePosition

func (pi *PositionIndex) NodePosition(
	node Node,
) Position

NodePosition returns the Position for the start of the given node. This is a convenience method equivalent to PositionAt(node.Start()).

func (*PositionIndex) NodesAt

func (pi *PositionIndex) NodesAt(
	offset int,
) []Node

NodesAt returns all nodes containing the given offset, ordered from outermost (root/document) to innermost (leaf/text). Returns an empty slice if the offset is outside all nodes.

Query time is O(log n + k) where k is the number of matching nodes.

func (*PositionIndex) NodesInRange

func (pi *PositionIndex) NodesInRange(
	start, end int,
) []Node

NodesInRange returns all nodes overlapping the given [start, end) range. A node overlaps if any part of its range intersects with [start, end).

The result includes nodes that: - Fully contain the range - Are fully contained within the range - Partially overlap the range

Query time is O(log n + k) where k is the number of overlapping nodes.

func (*PositionIndex) PositionAt

func (pi *PositionIndex) PositionAt(
	offset int,
) Position

PositionAt returns the Position (line, column, offset) for the given byte offset. This uses the integrated LineIndex for efficient conversion.

If no source was provided, returns a Position with just the offset.

func (*PositionIndex) Rebuild

func (pi *PositionIndex) Rebuild(
	root Node,
	source []byte,
)

Rebuild discards the current index and rebuilds it for the given root. This should be called when the AST has been modified.

func (*PositionIndex) Root

func (pi *PositionIndex) Root() Node

Root returns the root node of the indexed AST.

type Predicate

type Predicate func(Node) bool

Predicate is a function type for node matching predicates.

func All

func All(preds ...Predicate) Predicate

All returns a predicate that is true when all preds are true. Uses short-circuit evaluation: stops on first false. Returns true for empty predicate list.

func And

func And(p1, p2 Predicate) Predicate

And returns a predicate that is true when both p1 AND p2 are true. Uses short-circuit evaluation: p2 is not called if p1 is false.

func Any

func Any(preds ...Predicate) Predicate

Any returns a predicate that is true when any pred is true. Uses short-circuit evaluation: stops on first true. Returns false for empty predicate list.

func HasChild

func HasChild(pred Predicate) Predicate

HasChild returns a predicate that is true if any direct child matches pred. Only immediate children are checked (not descendants).

func HasDescendant

func HasDescendant(pred Predicate) Predicate

HasDescendant returns a predicate true if any descendant matches pred. All descendants are checked recursively.

func HasName

func HasName(name string) Predicate

HasName returns a predicate matching nodes with Name() == name. Works for NodeRequirement, NodeScenario, and any type implementing Named.

func InRange

func InRange(start, end int) Predicate

InRange returns a predicate matching nodes within [start, end). A node is in range if its span overlaps the given range. Overlap occurs when node.start < end AND node.end > start.

func IsType

func IsType[T Node]() Predicate

IsType returns a predicate that matches nodes of type T. Type checking uses Go type assertion.

func Not

func Not(p Predicate) Predicate

Not returns a predicate that negates p. Not(p)(node) equals !p(node).

func Or

func Or(p1, p2 Predicate) Predicate

Or returns a predicate that is true when p1 OR p2 is true. Uses short-circuit evaluation: p2 is not called if p1 is true.

type RenamedRequirement

type RenamedRequirement struct {
	// From is the original requirement name.
	From string

	// To is the new requirement name.
	To string

	// Node is the AST node if parsed from a requirement header.
	// May be nil if parsed from a list item.
	Node *NodeRequirement
}

RenamedRequirement represents a requirement rename operation. It contains the old name and the new name of the requirement.

type Requirement

type Requirement struct {
	// Name is the requirement name (text after "Requirement:").
	Name string

	// Section is the name of the parent section containing this requirement.
	Section string

	// Scenarios contains all scenarios within this requirement.
	Scenarios []*Scenario

	// Node is the underlying AST node for this requirement.
	Node *NodeRequirement
}

Requirement represents a parsed requirement.

func ExtractRequirements

func ExtractRequirements(
	content []byte,
) []*Requirement

ExtractRequirements parses content and returns all requirements.

func FindRequirement

func FindRequirement(
	content []byte,
	name string,
) (*Requirement, bool)

FindRequirement finds a requirement by name (case-insensitive). Returns the requirement and true if found, nil and false otherwise.

type Scenario

type Scenario struct {
	// Name is the scenario name (text after "Scenario:").
	Name string

	// Node is the underlying AST node for this scenario.
	Node *NodeScenario
}

Scenario represents a parsed scenario.

func FindScenario

func FindScenario(
	content []byte,
	name string,
) (*Scenario, bool)

FindScenario finds a scenario by name (case-insensitive). Returns the scenario and true if found, nil and false otherwise.

type Section

type Section struct {
	// Name is the section title text.
	Name string

	// Level is the header level (1-6).
	Level int

	// Start is the byte offset where the section starts.
	Start int

	// End is the byte offset where the section ends.
	End int

	// Content is the raw source content of the section.
	Content []byte

	// Node is the underlying AST node for this section.
	Node *NodeSection
}

Section represents a document section (header and its content).

func FindSection

func FindSection(
	content []byte,
	name string,
) (*Section, bool)

FindSection performs case-insensitive lookup for a section by name. Returns the section and true if found, nil and false otherwise.

type Spec

type Spec struct {
	// Root is the document node containing the entire AST.
	Root Node

	// Sections contains all sections indexed by their name (case-preserved).
	// Use FindSection for case-insensitive lookup.
	Sections map[string]*Section

	// Requirements contains all requirements found in the document.
	Requirements []*Requirement

	// Errors contains all parse errors encountered during parsing.
	Errors []ParseError
}

Spec represents a fully parsed specification file. It provides convenient access to sections, requirements, and parse errors.

type Token

type Token struct {
	// Type identifies the lexical category of this token.
	Type TokenType

	// Start is the byte offset from the beginning of the source.
	Start int

	// End is the byte offset past the last byte of this token (exclusive).
	// The token's content spans source[Start:End].
	End int

	// Source is a zero-copy slice view into the original source text.
	// It remains valid as long as the original source is retained.
	// For most tokens, this contains the token's text content.
	Source []byte

	// Message contains an error description for TokenError tokens.
	// For all other token types, this field is empty.
	Message string
}

Token represents a single lexical unit from the source text. It has position info and a zero-copy view into the original source.

func GetToken

func GetToken() *Token

GetToken retrieves a Token from the pool. The returned token's fields are zeroed and ready for use.

func (Token) IsBracket

func (t Token) IsBracket() bool

IsBracket returns true if the token is a bracket or parenthesis.

func (Token) IsDelimiter

func (t Token) IsDelimiter() bool

IsDelimiter returns true if the token is a punctuation delimiter.

func (Token) IsStructural

func (t Token) IsStructural() bool

IsStructural returns true if the token is structural (EOF, newline, etc).

func (Token) Len

func (t Token) Len() int

Len returns the byte length of the token.

func (Token) Text

func (t Token) Text() string

Text returns the token's source content as a string. This creates a copy; use Source directly for zero-copy access.

type TokenType

type TokenType uint8

TokenType represents the type of a lexical token. Each delimiter character has its own type for fine-grained tokenization, enabling maximum flexibility for error recovery and precise error messages.

const (

	// TokenEOF signals end of input. Start == End == len(source).
	TokenEOF TokenType = iota
	// TokenNewline represents a line ending (\n or \r\n normalized).
	TokenNewline
	// TokenWhitespace represents contiguous ASCII spaces or tabs.
	TokenWhitespace
	// TokenText represents plain text content (not a delimiter).
	TokenText
	// TokenError represents invalid input with an error message.
	TokenError

	// TokenHash represents a single '#' character.
	TokenHash
	// TokenAsterisk represents a single '*' character.
	TokenAsterisk
	// TokenUnderscore represents a single '_' character.
	TokenUnderscore
	// TokenTilde represents a single '~' character.
	TokenTilde
	// TokenBacktick represents a single '`' character.
	TokenBacktick
	// TokenDash represents a single '-' character.
	TokenDash
	// TokenPlus represents a single '+' character.
	TokenPlus
	// TokenDot represents a single '.' character.
	TokenDot
	// TokenColon represents a single ':' character.
	TokenColon
	// TokenPipe represents a single '|' character.
	TokenPipe

	// TokenBracketOpen represents a '[' character.
	TokenBracketOpen
	// TokenBracketClose represents a ']' character.
	TokenBracketClose
	// TokenParenOpen represents a '(' character.
	TokenParenOpen
	// TokenParenClose represents a ')' character.
	TokenParenClose
	// TokenGreaterThan represents a '>' character.
	TokenGreaterThan

	// TokenNumber represents a sequence of digits (for ordered lists).
	TokenNumber
	// TokenX represents 'x' or 'X' in checkbox syntax.
	TokenX
)

func (TokenType) String

func (t TokenType) String() string

String returns a human-readable name for the token type. This is useful for debugging and error messages.

type TransformAction

type TransformAction uint8

TransformAction signals the intended action for a node during transformation. It is returned by TransformVisitor methods to indicate whether to keep, replace, or delete the visited node.

const (
	// ActionKeep indicates the node should remain unchanged.
	// The returned Node value is ignored when this action is specified.
	ActionKeep TransformAction = iota

	// ActionReplace indicates the node should be replaced with the returned Node.
	// The returned Node will take the place of the original in the parent's children.
	ActionReplace

	// ActionDelete indicates the node should be removed from its parent's children.
	// The returned Node value is ignored when this action is specified.
	ActionDelete
)

func (TransformAction) String

func (a TransformAction) String() string

String returns a human-readable name for the transform action.

type TransformVisitor

type TransformVisitor interface {
	TransformDocument(
		*NodeDocument,
	) (Node, TransformAction, error)
	TransformSection(
		*NodeSection,
	) (Node, TransformAction, error)
	TransformRequirement(
		*NodeRequirement,
	) (Node, TransformAction, error)
	TransformScenario(
		*NodeScenario,
	) (Node, TransformAction, error)
	TransformParagraph(
		*NodeParagraph,
	) (Node, TransformAction, error)
	TransformList(
		*NodeList,
	) (Node, TransformAction, error)
	TransformListItem(
		*NodeListItem,
	) (Node, TransformAction, error)
	TransformCodeBlock(
		*NodeCodeBlock,
	) (Node, TransformAction, error)
	TransformBlockquote(
		*NodeBlockquote,
	) (Node, TransformAction, error)
	TransformText(
		*NodeText,
	) (Node, TransformAction, error)
	TransformStrong(
		*NodeStrong,
	) (Node, TransformAction, error)
	TransformEmphasis(
		*NodeEmphasis,
	) (Node, TransformAction, error)
	TransformStrikethrough(
		*NodeStrikethrough,
	) (Node, TransformAction, error)
	TransformCode(
		*NodeCode,
	) (Node, TransformAction, error)
	TransformLink(
		*NodeLink,
	) (Node, TransformAction, error)
	TransformLinkDef(
		*NodeLinkDef,
	) (Node, TransformAction, error)
	TransformWikilink(
		*NodeWikilink,
	) (Node, TransformAction, error)
}

TransformVisitor defines the interface for AST transformation via the visitor pattern. Each method receives a typed node and returns (replacement, action, error).

The Transform function uses post-order traversal, so children are transformed before their parent. This allows parent transform methods to see the results of child transformations.

When ActionKeep is returned, the replacement Node is ignored and the original is kept. When ActionReplace is returned, the replacement Node takes the place of the original. When ActionDelete is returned, the node is removed from its parent's children.

func AddScenario

func AddScenario(
	reqName string,
	scenario *NodeScenario,
) TransformVisitor

AddScenario creates a TransformVisitor that adds a scenario to the requirement matching reqName. The scenario is appended to the requirement's children. Other nodes pass through unchanged.

func Compose

func Compose(
	t1, t2 TransformVisitor,
) TransformVisitor

Compose creates a TransformVisitor that applies t1 first, then t2. The result of t1 is passed to t2, allowing sequential transformations.

func Filter

func Filter(
	pred func(Node) bool,
) TransformVisitor

Filter creates a TransformVisitor that deletes nodes where the predicate returns false. Nodes matching the predicate (returns true) are kept.

func Map

func Map(f func(Node) Node) TransformVisitor

Map creates a TransformVisitor that applies the given function to every node. If f returns the same node (by pointer equality), it is treated as ActionKeep. Otherwise, it is treated as ActionReplace with the returned node.

func Pipeline

func Pipeline(
	transforms ...TransformVisitor,
) TransformVisitor

Pipeline creates a TransformVisitor that applies all transforms in order. This is a convenience wrapper around multiple Compose calls.

func RemoveRequirement

func RemoveRequirement(
	name string,
) TransformVisitor

RemoveRequirement creates a TransformVisitor that deletes requirements matching the given name. The action is ActionDelete for matching requirements. Other nodes pass through unchanged.

func RenameRequirement

func RenameRequirement(
	oldName, newName string,
) TransformVisitor

RenameRequirement creates a TransformVisitor that renames requirements matching oldName to newName. Only requirements with Name() == oldName are affected; other nodes pass through unchanged.

func When

func When(
	pred func(Node) bool,
	transform TransformVisitor,
) TransformVisitor

When creates a conditional TransformVisitor that only applies the given transform when the predicate returns true. Nodes not matching the predicate pass through unchanged (ActionKeep).

type Visitor

type Visitor interface {
	VisitDocument(*NodeDocument) error
	VisitSection(*NodeSection) error
	VisitRequirement(*NodeRequirement) error
	VisitScenario(*NodeScenario) error
	VisitParagraph(*NodeParagraph) error
	VisitList(*NodeList) error
	VisitListItem(*NodeListItem) error
	VisitCodeBlock(*NodeCodeBlock) error
	VisitBlockquote(*NodeBlockquote) error
	VisitText(*NodeText) error
	VisitStrong(*NodeStrong) error
	VisitEmphasis(*NodeEmphasis) error
	VisitStrikethrough(*NodeStrikethrough) error
	VisitCode(*NodeCode) error
	VisitLink(*NodeLink) error
	VisitLinkDef(*NodeLinkDef) error
	VisitWikilink(*NodeWikilink) error
}

Visitor defines the interface for AST node visitors. Each method receives a typed node and returns an error to control traversal. Return nil to continue traversal, SkipChildren to skip children, or any other error to stop traversal immediately.

type VisitorContext

type VisitorContext struct {
	// contains filtered or unexported fields
}

VisitorContext provides context information during traversal, including access to the parent node and current depth.

func (*VisitorContext) Depth

func (c *VisitorContext) Depth() int

Depth returns the current depth in the tree. The root node has depth 0.

func (*VisitorContext) Parent

func (c *VisitorContext) Parent() Node

Parent returns the parent node of the current node being visited. Returns nil for the root node.

type Wikilink struct {
	// Target is the link target (e.g., "validation" or "changes/my-change").
	Target string

	// Display is the optional display text (empty if not specified).
	Display string

	// Anchor is the optional anchor/fragment (empty if not specified).
	Anchor string

	// Start is the byte offset where the wikilink starts.
	Start int

	// End is the byte offset where the wikilink ends.
	End int

	// Node is the underlying AST node for this wikilink.
	Node *NodeWikilink
}

Wikilink represents an extracted wikilink with parsed components.

func ExtractWikilinks(
	content []byte,
) []*Wikilink

ExtractWikilinks parses content and returns all wikilinks found. Uses a visitor to traverse the AST and collect all NodeWikilink nodes.

type WikilinkError

type WikilinkError struct {
	// Target is the wikilink target that failed to resolve.
	Target string

	// Display is the optional display text of the wikilink.
	Display string

	// Anchor is the optional anchor/fragment of the wikilink.
	Anchor string

	// Offset is the byte offset where the wikilink starts in the source.
	Offset int

	// Message describes why the wikilink is invalid.
	Message string
}

WikilinkError represents an error found during wikilink validation. It contains details about the broken wikilink and where it was found.

func ValidateWikilinks(
	root Node,
	_ []byte,
	projectRoot string,
) []WikilinkError

ValidateWikilinks checks all wikilinks in a parsed document and returns errors for any that cannot be resolved or have invalid anchors.

Parameters:

  • root: the root node of the parsed document
  • source: the original source bytes (reserved for future use)
  • projectRoot: the project root directory containing spectr/

Returns a slice of WikilinkError for each invalid wikilink found.

func (WikilinkError) Error

func (e WikilinkError) Error() string

Error implements the error interface.

Jump to

Keyboard shortcuts

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