region

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package region defines Båge's region-anchoring primitives: the content-anchored edit unit (SPEC §8) and the concurrency-safe resolver that maps a possibly-stale region back onto the live file (ADR-0003).

A region is addressed by a byte range plus a region_hash — the xxHash %016x of the region's NORMALIZED bytes (HYLLA_NODE_CONTRACT.md §4, byte-identical with Hylla so a whitespace-only reformat does not false-conflict). The hash does one job here: it verifies that the bytes at a candidate location are the block the edit targets. Relocation (a benign concurrent shift moved the region) is the CST's job: when the in-place hash no longer matches, the resolver reparses the live file and matches the region_hash against every node. Disambiguation of identical-content twins is NOT attempted — two matches are reported as Ambiguous and rejected rather than guessed. Båge over-rejects on purpose: corruption is never acceptable, a rejected edit is.

This package mirrors Hylla's per-node locator bundle minus graph identity (no parent_id / tail_symbol — Båge is ID-blind, SPEC §1.4); region_hash is the only seam, so file-mode and graph-mode resolve identically.

Index

Constants

View Source
const LineSentinel = -1

LineSentinel is the StartByte value marking a Region as line-addressed: the byte range is unknown and must be derived from StartLine/EndLine via a LineIndex (ResolveLines) before the region can be used.

Variables

This section is empty.

Functions

func HashRegion

func HashRegion(src []byte, start, end int) string

HashRegion returns the region_hash — the xxHash %016x of the NORMALIZED bytes of src[start:end] (HYLLA_NODE_CONTRACT §4), so it is byte-identical with Hylla's region_hash and a whitespace-only reformat of the block does not change it. It panics if the range is out of bounds, which signals a caller bug, not drift.

Types

type Edit

type Edit struct {
	// Region is the content-anchored target of the edit.
	Region Region
	// NewText is the replacement text for the region's bytes.
	NewText string
}

Edit is a region-anchored edit: replace the bytes of Region with NewText (SPEC §8.1). The model echoes a shown RegionHash; it never computes a hash or resends old text.

type EditResult

type EditResult struct {
	// Path is the file that was edited.
	Path string
	// ChangedStart is the inclusive starting byte offset of the changed range.
	ChangedStart int
	// ChangedEnd is the exclusive ending byte offset of the changed range.
	ChangedEnd int
	// NewRegionHash is the region_hash of the post-edit region bytes.
	NewRegionHash string
	// NewFileRawHash is the post-edit file RawHash.
	NewFileRawHash string
	// NewFileNormHash is the post-edit file NormHash.
	NewFileNormHash string
	// NewStartLine is the 1-based starting line of the post-edit region.
	NewStartLine int
	// NewEndLine is the 1-based ending line of the post-edit region.
	NewEndLine int
}

EditResult is the write-back contract to Hylla (SPEC §8.2): the changed byte range plus the recomputed region/file hashes and the new line range, so Hylla re-ingests only the changed region.

type FileAnchor

type FileAnchor struct {
	// Path is the file the anchor describes.
	Path string
	// RawHash is the xxHash %016x of the file's RAW bytes — the byte-offset gate.
	RawHash string
	// NormHash is the xxHash %016x of the file's normalized bytes — the drift
	// classifier.
	NormHash string
}

FileAnchor is the per-file drift gate (SPEC §8.1, HYLLA_NODE_CONTRACT.md §2): RawHash gates byte-offset validity; NormHash classifies whitespace-only drift.

type LineIndex

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

LineIndex maps between 1-based line numbers and byte offsets in a fixed source buffer. It records the byte offset at which each line begins, so line/col lookups and the line-addressed → byte-range resolution (SPEC §8.1, model-facing line addressing / byte-internal) are O(log n) or O(1).

Columns are 0-based BYTE offsets within a line — the UTF-8 byte-col convention of HYLLA_NODE_CONTRACT.md §1 (LSP converts to UTF-16 at its own boundary). Line endings are located by '\n'; a preceding '\r' (CRLF) stays part of the line's bytes, so byte offsets remain faithful to the raw file.

func NewLineIndex

func NewLineIndex(src []byte) LineIndex

NewLineIndex builds a LineIndex over src. The source is not retained; only line offsets and the length are kept.

func (LineIndex) ByteForLine

func (li LineIndex) ByteForLine(line int) int

ByteForLine returns the byte offset where the 1-based line begins. Lines below 1 clamp to the first line's offset (0); lines past the last clamp to the buffer size, so out-of-range line numbers never index out of bounds.

func (LineIndex) FillLineCols

func (li LineIndex) FillLineCols(r Region) Region

FillLineCols populates r.StartLine/EndLine and r.StartCol/EndCol from r.StartByte/EndByte (1-based lines, 0-based byte cols) and returns the updated Region. The end position is the point AT EndByte (the exclusive boundary), matching tree-sitter EndPoint semantics. It is a no-op-safe convenience for turning a byte-resolved region into a fully-populated locator bundle.

func (LineIndex) LineForByte

func (li LineIndex) LineForByte(off int) int

LineForByte returns the 1-based line containing the byte offset. Offsets below 0 clamp to line 1; offsets at or past the buffer size resolve to the last line. The boundary rule is half-open: the byte immediately after a '\n' belongs to the next line.

func (LineIndex) Lines

func (li LineIndex) Lines() int

Lines returns the number of lines. A buffer with a trailing '\n' counts the empty final line, matching the lineStarts layout.

func (LineIndex) PositionForByte

func (li LineIndex) PositionForByte(off int) (line, col int)

PositionForByte returns the 1-based line and 0-based byte column of off. The column is off minus the containing line's start offset.

func (LineIndex) ResolveLines

func (li LineIndex) ResolveLines(r Region) Region

ResolveLines turns a line-addressed Region (StartByte == LineSentinel) into a byte-range Region: StartByte becomes the start of StartLine and EndByte the start of the line AFTER EndLine (so the range covers EndLine in full, including its newline). The line/col fields are then refreshed from the resolved bytes. A Region that is already byte-addressed is returned unchanged.

Lines clamp via ByteForLine, so an over-large EndLine resolves to end-of-buffer rather than erroring; the caller's region_hash is the integrity check.

type Region

type Region struct {
	// Path is the file the region lives in.
	Path string
	// StartByte is the inclusive starting byte offset, or LineSentinel to mark
	// the region as line-addressed (resolve via StartLine/EndLine).
	StartByte int
	// EndByte is the exclusive ending byte offset.
	EndByte int
	// StartLine is the 1-based starting line.
	StartLine int
	// EndLine is the 1-based ending line.
	EndLine int
	// StartCol is the 0-based starting byte column within StartLine.
	StartCol int
	// EndCol is the 0-based ending byte column within EndLine.
	EndCol int
	// RegionHash is the xxHash %016x of the region's NORMALIZED bytes (matches
	// HYLLA_NODE_CONTRACT §4 so it is byte-identical with Hylla's), or "" when the
	// region carries no anchor (single-model file mode), in which case the given
	// byte range is treated as authoritative.
	RegionHash string
}

Region is a content-anchored locator into a file: a byte range, the corresponding line/col range, and the region_hash that anchors it by content (SPEC §8.1). It mirrors Hylla's per-node locator bundle minus graph identity.

Byte offsets are authoritative; line/col are derived conveniences. When StartByte is the LineSentinel value the region is line-addressed and the byte range must be resolved from the line range via a LineIndex before use.

type ResolveStatus

type ResolveStatus int

ResolveStatus reports how Resolve located a region against the live file.

const (
	// Exact means the region_hash matched the bytes at the region's own offset;
	// the range is used as-is.
	Exact ResolveStatus = iota
	// Shifted means a concurrent edit moved the region but not its content: the
	// region_hash matched exactly one node at a new offset (a benign shift).
	Shifted
	// Conflict means no live node matched the region_hash: the region's own
	// content changed — a hard reject.
	Conflict
	// Ambiguous means more than one live node matched the region_hash: identical
	// twins that this package refuses to guess between — a hard reject.
	Ambiguous
)

func Resolve

func Resolve(p parser.ParserPort, lang parser.Lang, live []byte, r Region) (start, end int, status ResolveStatus, err error)

Resolve maps r onto the live file bytes, returning the byte range to edit and how it was located (ADR-0003, the concurrency core). It is the resolve-under-lock step: callers hold the per-file lock and pass the current file bytes, so every edit sees prior concurrent commits.

Resolution:

  • If r.RegionHash == "" (no anchor, single-model file mode) the given byte range is authoritative and returned as Exact.
  • If HashRegion(live, r.StartByte, r.EndByte) == r.RegionHash the region is in place ⇒ Exact, used as-is.
  • Otherwise live is parsed under lang and every CST node whose region_hash equals r.RegionHash is collected: exactly one ⇒ Shifted (benign), its range is returned; zero ⇒ Conflict; more than one ⇒ Ambiguous. Conflict and Ambiguous return an error — Resolve never guesses and never misapplies.

A nil or empty-range region with a non-empty hash, or an in-place range that is out of bounds, falls through to the parse-and-match path rather than panicking, so a stale offset can never crash the resolver.

func (ResolveStatus) String

func (s ResolveStatus) String() string

String returns the lowercase name of the status.

Jump to

Keyboard shortcuts

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