garland

package module
v0.1.9 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 16 Imported by: 0

README

Garland

Garland: A rope-based text buffer library for Go, designed for text editors and applications requiring efficient document manipulation with version control.

If you use this, please support me on ko-fi: https://ko-fi.com/jeffday

ko-fi

Features

  • Rope data structure: O(log n) insert and delete operations using a balanced binary tree
  • Multiple addressing modes: Position by byte, rune (Unicode code point), or line:rune
  • Version control: Full undo/redo with branching support via forks and revisions
  • Three-tier storage: Automatic management across memory, warm (original file), and cold (external) storage
  • Decorations: Named position markers that track through edits
  • Multiple cursors: Independent cursor positions with automatic adjustment on edits
  • Search and replace: Literal and regex-based search with case sensitivity and whole-word options
  • Lazy loading: Stream large files with configurable ready thresholds
  • Transactions: Group operations into atomic revisions with rollback support
  • Memory management: Configurable soft/hard limits with LRU eviction to cold storage
  • File change detection: Monitor and handle external modifications to source files

Installation

go get github.com/phroun/garland

Requires Go 1.24 or later.

Usage

Basic Example
package main

import (
    "fmt"
    "github.com/phroun/garland"
)

func main() {
    // Initialize the library
    lib, err := garland.Init(garland.LibraryOptions{})
    if err != nil {
        panic(err)
    }

    // Create a new document
    g, err := lib.Open(garland.FileOptions{
        DataString: "Hello, World!",
    })
    if err != nil {
        panic(err)
    }
    defer g.Close()

    // Create a cursor and edit
    cursor := g.NewCursor()
    cursor.SeekByte(7)
    cursor.DeleteBytes(5, false)
    cursor.InsertString("Garland", nil, true)

    // Read the result
    cursor.SeekByte(0)
    content, _ := cursor.ReadBytes(g.ByteCount().Value)
    fmt.Println(string(content)) // "Hello, Garland!"
}
Opening Files
// From file path
g, err := lib.Open(garland.FileOptions{
    FilePath: "document.txt",
})

// From byte slice
g, err := lib.Open(garland.FileOptions{
    DataBytes: []byte("content"),
})

// From string
g, err := lib.Open(garland.FileOptions{
    DataString: "content",
})
Transactions
g.TransactionStart("Replace header")
cursor.SeekByte(0)
cursor.DeleteBytes(100, false)
cursor.InsertString("New Header\n", nil, true)
result, err := g.TransactionCommit()
// result.Revision contains the new revision number
Undo/Redo with Forks
// Undo to a previous revision
g.UndoSeek(5)

// Editing after undo creates a new fork automatically
cursor.InsertString("alternative content", nil, true)

// Switch between forks
g.ForkSeek(1, g.GetForkInfo(1).HighestRevision)
Decorations
// Add a decoration at cursor position
g.Decorate("bookmark", garland.AbsoluteAddress{
    Mode: garland.ByteMode,
    Byte: cursor.BytePos(),
})

// Query decoration position (updates as content changes)
pos, err := g.GetDecorationPosition("bookmark")

// Get decorations in a range
decorations := g.GetDecorationsInByteRange(0, 1000)
Search and Replace
// Find first occurrence
result, err := cursor.FindString("needle", garland.SearchOptions{
    CaseSensitive: false,
    WholeWord:     true,
})

// Replace all occurrences
count, err := cursor.ReplaceStringAll("old", "new", garland.SearchOptions{})

// Regex search
result, err := cursor.FindRegex(`\d{4}-\d{2}-\d{2}`, garland.SearchOptions{})
Storage Tiers
// Configure cold storage
lib, err := garland.Init(garland.LibraryOptions{
    ColdStoragePath: "/path/to/cold-storage",
})

// Use all storage tiers (memory + warm + cold)
g, err := lib.Open(garland.FileOptions{
    FilePath:     "large-file.txt",
    LoadingStyle: garland.AllStorage,
})

// Move inactive data to cold storage
g.Chill(garland.ChillInactiveForks)
Memory Management
lib, err := garland.Init(garland.LibraryOptions{
    MemorySoftLimit: 100 * 1024 * 1024, // 100 MB target
    MemoryHardLimit: 200 * 1024 * 1024, // 200 MB maximum
})

// Query memory usage
stats := g.MemoryUsage()

Architecture

Garland uses a rope data structure implemented as a balanced binary tree:

  • Leaf nodes store text data (default max 128KB per node)
  • Internal nodes aggregate metrics (byte count, rune count, line count)
  • Structural sharing enables memory-efficient versioning via copy-on-write

Each edit creates a new revision. Revisions are organized into forks, which branch when editing from a non-HEAD revision. This provides full undo history with the ability to explore alternative edit paths.

Storage Tiers

Tier Description Use Case
Memory Data held in RAM Active editing
Warm Original file on disk, verified by checksum Large files, read-heavy workloads
Cold Library-managed external storage Undo history, inactive forks

Transitions between tiers are automatic and transparent to the application.

REPL

An interactive REPL is included for testing and exploration:

go run ./cmd/garland-repl

Type help for available commands.

Testing

go test ./...

Tests are located alongside source files following Go conventions.

Benchmarking

A stress test tool is included that creates a 1GB test file and measures performance of common operations:

go run ./cmd/garland-bench

The benchmark tests:

  • File generation and opening (memory-only and tiered storage modes)
  • Seek and read operations across the file
  • Insert and delete operations at various sizes
  • Transaction cycles
  • Search operations
  • Undo/redo performance
  • Decoration operations
  • Memory management (chill to cold storage)

Expected runtime is 2-5 minutes on a modern machine (e.g., M2 MacBook). The benchmark creates temporary files that are automatically cleaned up on completion.

License

MIT License. See LICENSE for details.

Documentation

Overview

Package garland provides a rope-based data structure for efficient text/binary editing with multiple storage tiers, versioning, decorations, and lazy loading.

Index

Constants

View Source
const (
	// DefaultMaxLeafSize is the maximum bytes per leaf node.
	// Files larger than this are split into multiple leaves.
	DefaultMaxLeafSize = 128 * 1024 // 128KB

	// DefaultTargetLeafSize is the ideal leaf size (MaxLeafSize / 2).
	DefaultTargetLeafSize = 64 * 1024 // 64KB

	// DefaultMinLeafSize is the minimum leaf size before merging (MaxLeafSize / 4).
	DefaultMinLeafSize = 32 * 1024 // 32KB

	// DefaultInitialUsageWindow is the default byte range to keep in memory.
	DefaultInitialUsageWindow = 1024 * 1024 // 1MB
)

Default leaf size constants for tree building.

Variables

View Source
var (
	// ErrNotReady indicates that a position is not yet available during lazy loading.
	ErrNotReady = errors.New("position not yet available")

	// ErrInvalidPosition indicates that a position is out of bounds.
	ErrInvalidPosition = errors.New("position out of bounds")

	// ErrTimeout indicates that a blocking wait operation timed out.
	ErrTimeout = errors.New("operation timed out")

	// ErrInvalidUTF8 indicates that an operation would split a UTF-8 sequence.
	ErrInvalidUTF8 = errors.New("invalid UTF-8 sequence")

	// ErrOverlappingRanges indicates that source and destination ranges overlap
	// in an operation that doesn't allow overlap (e.g., Move).
	ErrOverlappingRanges = errors.New("source and destination ranges overlap")
)

Position errors

View Source
var (
	// ErrDecorationNotFound indicates that a decoration key does not exist.
	ErrDecorationNotFound = errors.New("decoration not found")

	// ErrInvalidDecorationKey indicates a decoration key with illegal
	// characters. RULING: keys are identifiers, not storage - ASCII
	// letters, digits, '_', '.', '#', and '-' only, non-empty. This
	// keeps every serialization of keys framing-safe by construction.
	ErrInvalidDecorationKey = errors.New("invalid decoration key: letters, digits, '_', '.', '#', '-' only")
)

Decoration errors

View Source
var (
	// ErrForkNotFound indicates that a fork ID does not exist.
	ErrForkNotFound = errors.New("fork not found")

	// ErrRevisionNotFound indicates that a revision does not exist in the current fork.
	ErrRevisionNotFound = errors.New("revision not found")
)

Versioning errors

View Source
var (
	// ErrColdStorageFailure indicates that a cold storage operation failed.
	ErrColdStorageFailure = errors.New("cold storage operation failed")

	// ErrWarmStorageMismatch indicates that the original file has changed (checksum mismatch).
	ErrWarmStorageMismatch = errors.New("warm storage checksum mismatch")

	// ErrReadOnly indicates that a region is read-only due to storage failure.
	ErrReadOnly = errors.New("region is read-only due to storage failure")

	// ErrNotFromOriginalFile indicates warm storage is not available for this node.
	ErrNotFromOriginalFile = errors.New("node is not from original file")

	// ErrNoRecoverySource indicates that no alternate known save
	// location could be verified and adopted (TryRecoverSource).
	ErrNoRecoverySource = errors.New("no alternate save location could be adopted")

	// ErrMemoryPressure indicates that memory limits are exceeded and cannot be reduced.
	// This occurs when hard memory limit is set but no cold storage is configured,
	// or when cold storage is full/unavailable. The application should handle this
	// by closing unused garlands, reducing operations, or configuring cold storage.
	ErrMemoryPressure = errors.New("memory limit exceeded and cannot be reduced")
)

Storage errors

View Source
var (
	// ErrNotSupported indicates that an optional file system operation is not supported.
	ErrNotSupported = errors.New("operation not supported")

	// ErrFileNotOpen indicates that the file handle is not open.
	ErrFileNotOpen = errors.New("file not open")
)

File system errors

View Source
var (
	// ErrRegionOverlap indicates that optimized regions cannot overlap.
	ErrRegionOverlap = errors.New("optimized regions cannot overlap")

	// ErrRegionNotFound indicates that the optimized region does not exist.
	ErrRegionNotFound = errors.New("optimized region not found")
)

Region errors

View Source
var (
	// ErrTransactionPending indicates that an operation is not allowed during a transaction.
	ErrTransactionPending = errors.New("operation not allowed during transaction")

	// ErrTransactionPoisoned indicates that a transaction was poisoned by an inner rollback.
	ErrTransactionPoisoned = errors.New("transaction was poisoned by inner rollback")

	// ErrNoTransaction indicates that there is no active transaction.
	ErrNoTransaction = errors.New("no active transaction")
)

Transaction errors

View Source
var (
	// ErrNotALeaf indicates that an operation expected a leaf node but got an internal node.
	ErrNotALeaf = errors.New("expected leaf node")

	// ErrInternal indicates an internal consistency error (should not happen).
	ErrInternal = errors.New("internal error")
)

Tree structure errors

View Source
var (
	// ErrNoDataSource indicates that no data source was provided in FileOptions.
	ErrNoDataSource = errors.New("no data source provided")

	// ErrMultipleDataSources indicates that multiple data sources were provided.
	ErrMultipleDataSources = errors.New("multiple data sources provided")

	// ErrNoColdStorage indicates that cold storage is required but not configured.
	ErrNoColdStorage = errors.New("cold storage not configured")

	// ErrDataNotLoaded indicates that data is in cold/warm storage and needs to be thawed.
	ErrDataNotLoaded = errors.New("data not loaded - call Thaw() first")
)

Configuration errors

View Source
var (
	// ErrCursorNotFound indicates that the cursor does not belong to this garland.
	ErrCursorNotFound = errors.New("cursor not found")
)

Cursor errors

Functions

func ValidDecorationKey added in v0.1.4

func ValidDecorationKey(key string) bool

ValidDecorationKey reports whether key is a legal decoration identifier: non-empty, ASCII letters and digits plus '_', '.', '#' (hashtag-style marks), and '-'. RULING: keys are identifiers, not storage in their own right - restricting them keeps every serialization of keys (e.g. cold-storage .dec blocks) framing-safe by construction, with no escaping anywhere.

Types

type AbsoluteAddress

type AbsoluteAddress struct {
	Mode AddressMode

	// Byte is used when Mode is ByteMode.
	Byte int64

	// Rune is used when Mode is RuneMode.
	Rune int64

	// Line is used when Mode is LineRuneMode (0-indexed line number).
	Line int64

	// LineRune is used when Mode is LineRuneMode (0-indexed rune within line).
	LineRune int64
}

AbsoluteAddress specifies a position using one of three addressing modes.

func ByteAddress

func ByteAddress(pos int64) AbsoluteAddress

ByteAddress creates an AbsoluteAddress in byte mode.

func LineAddress

func LineAddress(line, runeInLine int64) AbsoluteAddress

LineAddress creates an AbsoluteAddress in line:rune mode.

func RuneAddress

func RuneAddress(pos int64) AbsoluteAddress

RuneAddress creates an AbsoluteAddress in rune mode.

type AddressMode

type AddressMode int

AddressMode specifies how a position is interpreted.

const (
	// ByteMode specifies an absolute byte position (0-indexed).
	ByteMode AddressMode = iota

	// RuneMode specifies an absolute rune/Unicode code point position (0-indexed).
	RuneMode

	// LineRuneMode specifies a line number and rune position within that line (both 0-indexed).
	// The newline character is considered the last character of its line.
	LineRuneMode
)

type AppendPolicy

type AppendPolicy int

AppendPolicy controls how the library handles detected appends.

const (
	// AppendPolicyAsk notifies the application and waits for a decision.
	AppendPolicyAsk AppendPolicy = iota

	// AppendPolicyIgnore ignores this append but asks again next time.
	AppendPolicyIgnore

	// AppendPolicyNever ignores all future appends permanently.
	AppendPolicyNever

	// AppendPolicyOnce loads this append but asks again next time.
	AppendPolicyOnce

	// AppendPolicyContinuous keeps loading appends automatically (tail mode).
	AppendPolicyContinuous
)

type BackupInfo added in v0.1.7

type BackupInfo struct {
	State   BackupState
	Path    string // destination backup file (once armed and resolved)
	Subject string // the source file the backup captures
	Bytes   int64  // bytes copied so far / in the finished backup
	Err     string // failure detail when State == BackupFailed
}

BackupInfo reports the backup machinery's current standing.

type BackupOptions added in v0.1.7

type BackupOptions struct {
	// Name overrides the backup filename within the backup directory.
	// Default: "<source basename>~" (the emacs backup convention).
	// NOTE: never use the ".#<name>" form - that is the lock-file
	// namespace.
	Name string
}

BackupOptions configures SetBackupLocation.

type BackupState added in v0.1.7

type BackupState int

BackupState describes where the backup machinery stands.

const (
	// BackupDisabled: no backup location configured.
	BackupDisabled BackupState = iota

	// BackupArmed: configured; no modification seen yet, nothing
	// copied (viewing a file stays in this state forever).
	BackupArmed

	// BackupPending: a modification armed the copy; it has not
	// finished yet.
	BackupPending

	// BackupReady: the backup is in place at the destination.
	BackupReady

	// BackupCommitted: a save overwrote the protected file; the backup
	// is kept (survives Close).
	BackupCommitted

	// BackupFailed: the copy failed (see BackupInfo.Err). Saves are
	// never blocked by a failed backup - the app decides what to do.
	BackupFailed
)

func (BackupState) String added in v0.1.7

func (s BackupState) String() string

String returns a human-readable description of the state.

type ByteBufferRegion

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

ByteBufferRegion is a simple OptimizedRegion implementation using a byte slice. It's suitable for small to medium editing sessions.

func NewByteBufferRegion

func NewByteBufferRegion(initialContent []byte) *ByteBufferRegion

NewByteBufferRegion creates a new ByteBufferRegion with initial content.

func (*ByteBufferRegion) ByteCount

func (r *ByteBufferRegion) ByteCount() int64

ByteCount returns the number of bytes in the region.

func (*ByteBufferRegion) Content

func (r *ByteBufferRegion) Content() []byte

Content returns the full content of the region.

func (*ByteBufferRegion) DeleteBytes

func (r *ByteBufferRegion) DeleteBytes(offset, length int64) error

DeleteBytes deletes length bytes starting at offset.

func (*ByteBufferRegion) InsertBytes

func (r *ByteBufferRegion) InsertBytes(offset int64, data []byte) error

InsertBytes inserts data at the given offset.

func (*ByteBufferRegion) LineCount

func (r *ByteBufferRegion) LineCount() int64

LineCount returns the number of newlines in the region.

func (*ByteBufferRegion) ReadBytes

func (r *ByteBufferRegion) ReadBytes(offset, length int64) ([]byte, error)

ReadBytes reads length bytes starting at offset.

func (*ByteBufferRegion) RuneCount

func (r *ByteBufferRegion) RuneCount() int64

RuneCount returns the number of runes in the region.

type CacheTier

type CacheTier int

CacheTier indicates the priority level of a cached decoration.

const (
	// CacheTierWarm is for decorations seen during traversal but not actively used.
	CacheTierWarm CacheTier = iota
	// CacheTierHot is for decorations actively requested by the application.
	CacheTierHot
)

type ChangeResult

type ChangeResult struct {
	Fork     ForkID
	Revision RevisionID
}

ChangeResult contains version information after a mutation.

type ChillLevel

type ChillLevel int

ChillLevel specifies how aggressively to move data to cold storage.

const (
	// ChillInactiveForks moves data not used by the currently active fork
	// to cold storage. This is the gentlest level.
	ChillInactiveForks ChillLevel = iota

	// ChillOldHistory also moves data from older revisions in the undo
	// buffer that are more than a few steps back and not utilized by
	// any branching point.
	ChillOldHistory

	// ChillUnusedData moves all data not used at the current undo position.
	// This keeps only what's needed to display/edit the current state.
	ChillUnusedData

	// ChillEverything moves all data to cold storage. Use when switching
	// to another document or dropping to a shell. Data will be restored
	// from cold storage on access.
	ChillEverything
)

type ColdStorageInterface

type ColdStorageInterface interface {
	// Set stores data for a block within a folder.
	// Folder names are unique per loaded file.
	Set(folder, block string, data []byte) error

	// Get retrieves data for a block within a folder.
	Get(folder, block string) ([]byte, error)

	// Delete removes a block from a folder.
	Delete(folder, block string) error

	// DeleteFolder removes an empty folder.
	// Returns an error if the folder is not empty.
	DeleteFolder(folder string) error
}

ColdStorageInterface allows custom cold storage implementations.

type CopyResult

type CopyResult struct {
	ChangeResult
	DisplacedDecorations []RelativeDecoration // Decorations that were in the destination range (original positions)
}

CopyResult contains the result of a Copy operation.

type CountResult

type CountResult struct {
	Value    int64
	Complete bool // true if EOF has been reached
}

CountResult contains a count and whether it is complete.

type Cursor

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

Cursor represents a position within a Garland with its own ready state. Cursors automatically update when content changes before their position.

func (*Cursor) BackDeleteBytes

func (c *Cursor) BackDeleteBytes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

BackDeleteBytes deletes `length` bytes BEFORE the cursor position. Cursor moves to the start of the deleted range (its new position). Returns decorations from the deleted range.

func (*Cursor) BackDeleteRunes

func (c *Cursor) BackDeleteRunes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

BackDeleteRunes deletes `length` runes BEFORE the cursor position. Cursor moves to the start of the deleted range (its new position). Returns decorations from the deleted range.

func (*Cursor) BeginOptimizedRegion

func (c *Cursor) BeginOptimizedRegion(startByte, endByte int64) error

BeginOptimizedRegion explicitly starts an optimized region at the specified bounds.

QUARANTINED: optimized regions are unfinished scaffolding. The edit paths never route through an active region, so a region created here holds FIXED bounds that go stale on the next checkpoint/transaction - a live corruption hazard. Until the feature is either fully wired in or removed, this returns ErrNotSupported. (RULING 2026-07-12: keep the scaffolding, block the entry point.)

func (*Cursor) BytePos

func (c *Cursor) BytePos() int64

BytePos returns the cursor's absolute byte position. Concurrency: cursor fields are only written under the garland write lock (seeks, edits adjusting passive cursors), so accessors synchronize through it.

func (*Cursor) CopyBytes

func (c *Cursor) CopyBytes(srcStart, srcEnd, dstStart, dstEnd int64, decorationsToAdd []RelativeDecoration, insertBefore bool) (CopyResult, error)

CopyBytes copies a byte range to a new location. All four addresses are interpreted in the document AS IT STANDS AT THE MOMENT OF THIS CALL (see MoveBytes; the operation compensates for its own intermediate shifts, never the caller). Source and destination ranges may overlap for Copy (source is snapshotted first). - srcStart, srcEnd: source byte range [srcStart, srcEnd) - dstStart, dstEnd: destination byte range to replace [dstStart, dstEnd) - decorationsToAdd: decorations to add to the copied content (like Insert) - insertBefore: if true, displaced decorations consolidate to end of new content Returns CopyResult with the displaced decorations from the destination range.

func (*Cursor) CountRegex

func (c *Cursor) CountRegex(pattern string, caseInsensitive bool) (int, error)

CountRegex counts regex matches in the document.

func (*Cursor) CountString

func (c *Cursor) CountString(needle string, opts SearchOptions) (int, error)

CountString counts occurrences of needle in the document.

func (*Cursor) DeleteBytes

func (c *Cursor) DeleteBytes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

DeleteBytes deletes `length` bytes starting at cursor position. Returns decorations from the deleted range. If includeLineDecorations is true, also returns (but does not move) decorations from partially affected lines.

func (*Cursor) DeleteRunes

func (c *Cursor) DeleteRunes(length int64, includeLineDecorations bool) ([]RelativeDecoration, ChangeResult, error)

DeleteRunes deletes `length` runes starting at cursor position. Returns decorations from the deleted range. If includeLineDecorations is true, also returns (but does not move) decorations from partially affected lines.

func (*Cursor) FindNext

func (c *Cursor) FindNext(needle string, opts SearchOptions) (*SearchResult, error)

FindNext finds the next occurrence and moves cursor to it. Returns the match or nil if not found.

func (*Cursor) FindNextRegex

func (c *Cursor) FindNextRegex(pattern string, opts RegexOptions) (*SearchResult, error)

FindNextRegex finds the next regex match and moves cursor to it.

func (*Cursor) FindRegex

func (c *Cursor) FindRegex(pattern string, opts RegexOptions) (*SearchResult, error)

FindRegex searches for a regex pattern starting from the cursor position. Returns the first match found, or nil if no match. The cursor is NOT moved by this operation.

func (*Cursor) FindRegexAll

func (c *Cursor) FindRegexAll(pattern string, opts RegexOptions) ([]SearchResult, error)

FindRegexAll finds all regex matches in the document.

func (*Cursor) FindString

func (c *Cursor) FindString(needle string, opts SearchOptions) (*SearchResult, error)

FindString searches for a string starting from the cursor position. Returns the first match found, or nil if no match. The cursor is NOT moved by this operation.

func (*Cursor) FindStringAll

func (c *Cursor) FindStringAll(needle string, opts SearchOptions) ([]SearchResult, error)

FindStringAll finds all occurrences of a string in the document. Returns all matches in document order (or reverse order if Backward).

func (*Cursor) HasOptimizedRegion

func (c *Cursor) HasOptimizedRegion() bool

HasOptimizedRegion returns true if the cursor has an active optimized region.

func (*Cursor) InsertBytes

func (c *Cursor) InsertBytes(data []byte, decorations []RelativeDecoration, insertBefore bool) (ChangeResult, error)

InsertBytes inserts raw bytes at the cursor position. If insertBefore is true, insertion occurs before any existing cursors/decorations at this position; otherwise after. After insertion, cursor advances to the end of the inserted content.

func (*Cursor) InsertString

func (c *Cursor) InsertString(data string, decorations []RelativeDecoration, insertBefore bool) (ChangeResult, error)

InsertString inserts a string at the cursor position. Relative decoration positions are measured in runes. If insertBefore is true, insertion occurs before any existing cursors/decorations at this position; otherwise after. After insertion, cursor advances to the end of the inserted content.

func (*Cursor) IsReady

func (c *Cursor) IsReady() bool

IsReady returns true if the read-ahead threshold has been met relative to this cursor's position.

func (*Cursor) LinePos

func (c *Cursor) LinePos() (line, runeInLine int64)

LinePos returns the cursor's line number and rune position within that line. Both values are 0-indexed.

func (*Cursor) MatchRegex

func (c *Cursor) MatchRegex(pattern string, caseInsensitive bool) (bool, *SearchResult, error)

MatchRegex checks if the regex matches at the current cursor position. Returns true if the pattern matches starting exactly at cursor position.

func (*Cursor) Mode

func (c *Cursor) Mode() CursorMode

Mode returns the cursor's current mode.

func (*Cursor) MoveBytes

func (c *Cursor) MoveBytes(srcStart, srcEnd, dstStart, dstEnd int64, insertBefore bool) (MoveResult, error)

MoveBytes moves a byte range to a new location. All four addresses are interpreted in the document AS IT STANDS AT THE MOMENT OF THIS CALL: the operation is internally composite (extract, delete destination, insert), and the implementation adjusts for its own intermediate shifts - the caller never compensates. (Not "as opened": prior edits are already reflected.) Source and destination ranges cannot overlap for Move. Decorations in the source range move with the content. Decorations in the destination range are consolidated and returned. - srcStart, srcEnd: source byte range [srcStart, srcEnd) - dstStart, dstEnd: destination byte range to replace [dstStart, dstEnd) - insertBefore: if true, displaced decorations consolidate to end of new content Returns MoveResult with the displaced decorations from the destination range.

func (*Cursor) OptimizedRegionBounds

func (c *Cursor) OptimizedRegionBounds() (start, end int64, ok bool)

OptimizedRegionBounds returns the content bounds of the active region. Returns (0, 0, false) if no region is active.

func (*Cursor) OptimizedRegionGraceWindow

func (c *Cursor) OptimizedRegionGraceWindow() (start, end int64, ok bool)

OptimizedRegionGraceWindow returns the grace window bounds of the active region. Returns (0, 0, false) if no region is active.

func (*Cursor) OptimizedRegionSerial

func (c *Cursor) OptimizedRegionSerial() int64

OptimizedRegionSerial returns the serial number of the cursor's active region, or -1 if no region is active. Useful for debugging region lifecycle.

func (*Cursor) OverwriteBytes

func (c *Cursor) OverwriteBytes(length int64, newData []byte) ([]RelativeDecoration, ChangeResult, error)

OverwriteBytes replaces `length` bytes at cursor position with new data. This is more efficient than separate delete + insert for binary editing. The operation properly accounts for changes in line counts (newlines) and rune counts (UTF-8 sequences). Returns decorations that were in the overwritten range. Cursor position is not changed after the operation.

func (*Cursor) OverwriteBytesWithDecorations

func (c *Cursor) OverwriteBytesWithDecorations(length int64, newData []byte, decorationsToAdd []RelativeDecoration, insertBefore bool) ([]RelativeDecoration, ChangeResult, error)

OverwriteBytesWithDecorations replaces bytes with new data, adding decorations. - decorationsToAdd: decorations to add to the new content (relative to new content start) - insertBefore: if true, displaced decorations consolidate to end; if false, to start Returns the original decorations from the overwritten range with their original relative positions.

func (*Cursor) Position

func (c *Cursor) Position() CursorPosition

Position returns the cursor's position in all coordinate systems.

func (*Cursor) ReadBytes

func (c *Cursor) ReadBytes(length int64) ([]byte, error)

ReadBytes reads `length` bytes starting at cursor position. After reading, cursor advances past the read data.

func (*Cursor) ReadLine

func (c *Cursor) ReadLine() (string, error)

ReadLine reads the entire line the cursor is on. Note: Does NOT advance cursor (line-oriented reading is typically peek-like).

func (*Cursor) ReadString

func (c *Cursor) ReadString(length int64) (string, error)

ReadString reads `length` runes starting at cursor position as a string. After reading, cursor advances past the read data.

func (*Cursor) ReplaceRegex

func (c *Cursor) ReplaceRegex(pattern, replacement string, opts RegexOptions) (bool, ChangeResult, error)

ReplaceRegex replaces the first regex match with replacement. Replacement can include $1, $2, etc. for capture groups.

func (*Cursor) ReplaceRegexAll

func (c *Cursor) ReplaceRegexAll(pattern, replacement string, opts RegexOptions) (int, ChangeResult, error)

ReplaceRegexAll replaces all regex matches with replacement.

func (*Cursor) ReplaceRegexCount

func (c *Cursor) ReplaceRegexCount(pattern, replacement string, count int, opts RegexOptions) (int, ChangeResult, error)

ReplaceRegexCount replaces up to count regex matches with replacement.

func (*Cursor) ReplaceString

func (c *Cursor) ReplaceString(needle, replacement string, opts SearchOptions) (bool, ChangeResult, error)

ReplaceString replaces the first occurrence of needle with replacement. Search starts from cursor position. Returns the change result and whether a replacement was made.

func (*Cursor) ReplaceStringAll

func (c *Cursor) ReplaceStringAll(needle, replacement string, opts SearchOptions) (int, ChangeResult, error)

ReplaceStringAll replaces all occurrences of needle with replacement. Returns the number of replacements made.

func (*Cursor) ReplaceStringCount

func (c *Cursor) ReplaceStringCount(needle, replacement string, count int, opts SearchOptions) (int, ChangeResult, error)

ReplaceStringCount replaces up to count occurrences of needle with replacement. If count is -1, replaces all occurrences. Returns the number of replacements made.

func (*Cursor) RunePos

func (c *Cursor) RunePos() int64

RunePos returns the cursor's absolute rune position.

func (*Cursor) SeekByWord

func (c *Cursor) SeekByWord(n int) (int, error)

SeekByWord moves the cursor by n words using WordStyleSimple. Positive n moves forward, negative n moves backward. Returns the number of words actually moved (may be less than requested at the buffer boundaries). Use SeekByWordStyle to choose different word semantics.

func (*Cursor) SeekByWordStyle added in v0.1.4

func (c *Cursor) SeekByWordStyle(n int, style WordStyle) (int, error)

SeekByWordStyle moves the cursor by n words under the given WordStyle. Positive n moves forward, negative n moves backward. Returns the number of words actually moved.

func (*Cursor) SeekByte

func (c *Cursor) SeekByte(pos int64) error

SeekByte moves the cursor to an absolute byte position. Blocks indefinitely until the position is available during lazy loading. Use SeekByteWithTimeout for timeout control, or check IsByteReady first for non-blocking.

func (*Cursor) SeekByteWithTimeout

func (c *Cursor) SeekByteWithTimeout(pos int64, timeout time.Duration) error

SeekByteWithTimeout moves the cursor to an absolute byte position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SeekLine

func (c *Cursor) SeekLine(line, runeInLine int64) error

SeekLine moves the cursor to a line and rune-within-line position. Line and rune are both 0-indexed. The newline is the last character of its line. Blocks indefinitely until the position is available during lazy loading. Use SeekLineWithTimeout for timeout control, or check IsLineReady first for non-blocking.

func (*Cursor) SeekLineEnd

func (c *Cursor) SeekLineEnd() error

SeekLineEnd moves the cursor to the end of the current line. The cursor is positioned after the last character before the newline (or at EOF).

func (*Cursor) SeekLineStart

func (c *Cursor) SeekLineStart() error

SeekLineStart moves the cursor to the beginning of the current line.

func (*Cursor) SeekLineWithTimeout

func (c *Cursor) SeekLineWithTimeout(line, runeInLine int64, timeout time.Duration) error

SeekLineWithTimeout moves the cursor to a line and rune-within-line position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SeekRelativeBytes

func (c *Cursor) SeekRelativeBytes(delta int64) error

SeekRelativeBytes moves the cursor relative to its current byte position. Positive delta moves forward, negative moves backward. Clamps to valid range [0, byteCount].

func (*Cursor) SeekRelativeRunes

func (c *Cursor) SeekRelativeRunes(delta int64) error

SeekRelativeRunes moves the cursor relative to its current rune position. Positive delta moves forward, negative moves backward. Clamps to valid range [0, runeCount].

func (*Cursor) SeekRune

func (c *Cursor) SeekRune(pos int64) error

SeekRune moves the cursor to an absolute rune position. Blocks indefinitely until the position is available during lazy loading. Use SeekRuneWithTimeout for timeout control, or check IsRuneReady first for non-blocking.

func (*Cursor) SeekRuneWithTimeout

func (c *Cursor) SeekRuneWithTimeout(pos int64, timeout time.Duration) error

SeekRuneWithTimeout moves the cursor to an absolute rune position with timeout control. If timeout is 0, returns ErrNotReady immediately if position not available. If timeout is negative, blocks indefinitely. If timeout is positive, waits up to that duration before returning ErrTimeout.

func (*Cursor) SetMode

func (c *Cursor) SetMode(mode CursorMode)

SetMode sets the cursor's mode. Changing mode does not affect any currently active optimized region.

func (*Cursor) SetTracksHistory added in v0.1.5

func (c *Cursor) SetTracksHistory(track bool)

SetTracksHistory turns per-revision position tracking on or off for this cursor. Turning it off drops any history already accumulated (freeing it) and stops future recording - the cursor still adjusts to edits but is no longer teleported to historical positions on a seek. Turning it back on resumes recording from the current version.

func (*Cursor) TracksHistory added in v0.1.5

func (c *Cursor) TracksHistory() bool

TracksHistory reports whether this cursor records per-revision positions and is restored on undo/redo/fork navigation.

func (*Cursor) TruncateToEOF

func (c *Cursor) TruncateToEOF() (ChangeResult, error)

TruncateToEOF deletes everything from cursor position to end of file.

func (*Cursor) WaitReady

func (c *Cursor) WaitReady() error

WaitReady blocks until the cursor becomes ready.

type CursorMode

type CursorMode int

CursorMode determines how a cursor interacts with optimized regions.

const (
	// CursorModeHuman automatically creates optimized regions on edit operations.
	// This is the default mode for interactive editing.
	CursorModeHuman CursorMode = iota

	// CursorModeProcess does not auto-create regions; uses transactions instead.
	// Use this mode for programmatic/scripted operations.
	CursorModeProcess
)

type CursorPosition

type CursorPosition struct {
	BytePos  int64
	RunePos  int64
	Line     int64
	LineRune int64
}

CursorPosition stores a cursor's position in all coordinate systems.

type Decoration

type Decoration struct {
	Key      string
	Position int64 // relative byte offset within the node
}

Decoration represents a decoration stored within a node.

type DecorationCacheEntry

type DecorationCacheEntry struct {
	// Last known location (hint for search)
	LastKnownFork   ForkID
	LastKnownRev    RevisionID
	LastKnownNode   NodeID // 0 means "confirmed not present at this fork/revision"
	LastKnownOffset int64

	// Cache management
	Tier       CacheTier // Hot = actively used, Warm = seen during traversal
	LastAccess time.Time
}

DecorationCacheEntry caches the last known location of a decoration. The presence of an entry indicates the key has been used at some point. The cached location is a hint - it may be stale if fork/revision differs.

type DecorationEntry

type DecorationEntry struct {
	Key     string
	Address *AbsoluteAddress // nil to delete the decoration
}

DecorationEntry represents a decoration with its absolute position.

type DeviceInfo added in v0.1.7

type DeviceInfo struct {
	// DeviceID identifies the device/volume holding the path (on local
	// unix filesystems "dev<dev>", on Windows the volume name). Two
	// equal non-empty IDs mean the same device. Empty means unknown.
	DeviceID string

	// FreeBytes is the space available to new writes, -1 if unknown.
	FreeBytes int64

	// TotalBytes is the device's total capacity, -1 if unknown.
	TotalBytes int64
}

DeviceInfo describes the storage device/volume behind a path, for free-space warnings ("this save may not fit") and for recognizing when two paths live on different media (e.g. saving to removable media vs. the working drive).

type DivergenceDirection

type DivergenceDirection int

DivergenceDirection indicates the relationship of a fork to the current fork.

const (
	// BranchedFrom means the current fork split off from the specified fork.
	BranchedFrom DivergenceDirection = iota
	// BranchedInto means the specified fork split off from the current fork.
	BranchedInto
)

type FileHandle

type FileHandle interface{}

FileHandle represents an open file.

type FileMetadata added in v0.1.7

type FileMetadata struct {
	// Exists is false when the path currently names no file. The other
	// fields are meaningless in that case.
	Exists bool

	// Size is the file's length in bytes.
	Size int64

	// ModTime is the file's last-modification time.
	ModTime time.Time

	// Identity names the underlying storage object, independent of the
	// path (on local unix filesystems "dev<dev>:ino<inode>"). Two
	// different non-empty identities for the same path mean the path
	// was re-bound to a different object - the classic write-temp-and-
	// rename "file replaced" case. Empty means unknown; identity
	// comparison is then skipped and detection falls back to
	// size + mtime.
	Identity string
}

FileMetadata describes a file as observed at one moment: the information Garland tracks to detect external modification of a source file between opening it and saving over it. A virtualized filesystem supplies these through FileSystemInterface.Stat, or the application volunteers them via Garland.ReportSourceMetadata when it learns fresher facts than Garland could observe itself.

type FileOptions

type FileOptions struct {
	// LoadingStyle determines storage tier availability
	LoadingStyle LoadingStyle

	// Data source (exactly one must be provided)
	FilePath    string              // load from file path using default FS
	FileSystem  FileSystemInterface // custom file system (use with FilePath)
	DataBytes   []byte              // literal byte content
	DataString  string              // literal string content
	DataChannel chan []byte         // streaming input

	// Initial decorations (optional, at most one)
	Decorations      []DecorationEntry // literal list
	DecorationChan   chan DecorationEntry
	DecorationPath   string // load from dump file
	DecorationString string // parse from dump format

	// Tree structure options
	// MaxLeafSize is the maximum bytes per leaf node (default 128KB).
	// Larger files are split into a balanced tree of leaves.
	// Target leaf size is MaxLeafSize/2, minimum is MaxLeafSize/4.
	MaxLeafSize int64

	// InitialUsageStart and InitialUsageEnd define a byte range to keep in memory.
	// Nodes outside this range are immediately chilled to cold storage after loading.
	// This avoids loading a huge file fully into RAM just to chill it immediately.
	// Set InitialUsageEnd to -1 (default) to use a reasonable default window.
	// Set both to 0 to chill everything immediately (pure cold storage load).
	InitialUsageStart int64
	InitialUsageEnd   int64 // -1 means auto (defaults to 1MB or file size, whichever is smaller)

	// Ready thresholds - ALL specified (non-zero) must be met
	// Measured from beginning of file at initial load
	ReadyLines int64
	ReadyBytes int64
	ReadyRunes int64
	ReadyAll   bool

	// Lazy read-ahead - ALL specified (non-zero) must be met
	// Measured from highest seek position after any seek
	ReadAheadLines int64
	ReadAheadBytes int64
	ReadAheadRunes int64
	ReadAheadAll   bool

	// UseEmacsLocks (opt-in, file sources only) maintains an
	// emacs-compatible ".#<name>" lock file next to the source for as
	// long as the buffer holds unsaved modifications, so emacs (and
	// tools honoring its protocol) warn users off the file - and
	// Garland reports foreign locks it finds (SourceLockOwner,
	// SourceConsistencyReport.LockedBy). See emacs_lock.go.
	UseEmacsLocks bool

	// LockOwner overrides the identity written inside the emacs lock
	// file (and used to recognize our own lock vs. a foreign one).
	// Empty means the default "user@host.pid", derived from the
	// environment. Follow that form for emacs interoperability; the
	// value is used verbatim after trimming surrounding whitespace,
	// and must be a single line. Only meaningful with UseEmacsLocks.
	LockOwner string
}

FileOptions configures how a Garland is opened.

type FileSystemInterface

type FileSystemInterface interface {
	// Required methods
	Open(name string, mode OpenMode) (FileHandle, error)
	SeekByte(handle FileHandle, pos int64) error
	ReadBytes(handle FileHandle, length int) ([]byte, error)
	IsEOF(handle FileHandle) bool
	Close(handle FileHandle) error

	// Optional methods (may return ErrNotSupported)
	HasChanged(handle FileHandle) (bool, error)
	FileSize(handle FileHandle) (int64, error)
	BlockChecksum(handle FileHandle, start, length int64) ([]byte, error)
	WriteBytes(handle FileHandle, data []byte) error
	Truncate(handle FileHandle, size int64) error

	// Convenience methods for file operations
	WriteFile(name string, data []byte) error
	ReadFile(name string) ([]byte, error)

	// Directory operations
	MkdirAll(path string) error
	Remove(name string) error
	Rmdir(path string) error // Only removes empty directories

	// Rename atomically replaces newpath with oldpath. Cold storage
	// relies on this so a block being re-written (chill) can never be
	// torn-read by a concurrent Get (unlocked save phase, thaw).
	Rename(oldpath, newpath string) error

	// Stat reports a path's current metadata for external-modification
	// detection. A missing file is NOT an error: report it as
	// FileMetadata{Exists: false} with a nil error; errors are reserved
	// for real failures. Implementations without metadata may return
	// ErrNotSupported - Garland then tracks only what the application
	// volunteers through ReportSourceMetadata.
	Stat(name string) (FileMetadata, error)

	// DeviceInfo reports the storage device behind a path (identity and
	// free space), for save-time free-space warnings. May return
	// ErrNotSupported.
	DeviceInfo(name string) (DeviceInfo, error)
}

FileSystemInterface abstracts file operations for custom protocols. The library provides a default implementation for local files.

func NewLocalFileSystem added in v0.1.6

func NewLocalFileSystem() FileSystemInterface

NewLocalFileSystem returns a FileSystemInterface backed by the real operating-system filesystem - the same implementation Garland uses by default. Hosts that want to pass an explicit filesystem to SaveAs, or wrap/partially delegate to local disk when building a custom FileSystemInterface, can obtain one here instead of reimplementing the whole interface. (SaveAs also accepts a nil filesystem, which resolves to this automatically.)

type ForkDivergence

type ForkDivergence struct {
	Fork          ForkID
	DivergenceRev RevisionID          // revision at which split occurred
	Direction     DivergenceDirection // relationship to current fork
}

ForkDivergence describes where a fork split occurred.

type ForkID

type ForkID uint64

ForkID uniquely identifies a fork within a Garland.

type ForkInfo

type ForkInfo struct {
	ID              ForkID
	ParentFork      ForkID
	ParentRevision  RevisionID // revision at which this fork split from parent
	HighestRevision RevisionID
	PrunedUpTo      RevisionID // revisions < this have been pruned from this fork's view
	Deleted         bool       // true if fork is soft-deleted (data may still exist for child forks)
}

ForkInfo contains metadata about a fork.

type ForkRevision

type ForkRevision struct {
	Fork     ForkID
	Revision RevisionID
}

ForkRevision is a composite key for looking up versioned state.

type Garland

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

Garland is the main data structure representing an editable file.

func (*Garland) AcknowledgeSourceChange

func (g *Garland) AcknowledgeSourceChange(reload bool) error

AcknowledgeSourceChange acknowledges a detected source change. Call this after the user has been notified and made a decision.

func (*Garland) AdoptWarmSource added in v0.1.7

func (g *Garland) AdoptWarmSource(fs FileSystemInterface, name string, level VerifyLevel) error

AdoptWarmSource switches the buffer's source (warm-storage backing) to another file that is believed to hold exactly the current content - swiftly and seamlessly, with the cheapest sufficient consistency check chosen by level. On success every current leaf is re-homed onto the new file, undo history that referenced the old source is migrated off it (cold storage when available, memory otherwise; unreachable history is marked lost, never silently corrupted), and change detection re-baselines against the new file.

A nil fs resolves like SaveAs: the current source filesystem, else the library default. Returns ErrWarmStorageMismatch when the candidate cannot be proven consistent at the requested level.

func (*Garland) BackupInfo added in v0.1.7

func (g *Garland) BackupInfo() BackupInfo

BackupInfo reports the backup machinery's current standing. Cheap; safe to call from a status-bar paint path.

func (*Garland) Bake added in v0.1.8

func (g *Garland) Bake()

Bake forces a hard edge: the current coalescing run (if any) is finalized, and the next edit starts a fresh history entry no matter how adjacent it is. Safe to call at any time, including when coalescing is disabled or no run is active.

func (*Garland) BreakSourceLock added in v0.1.7

func (g *Garland) BreakSourceLock() error

BreakSourceLock force-removes a foreign emacs-style lock file on the source - the "steal the lock" choice after warning the user - and, when the buffer holds unsaved modifications, immediately acquires the lock for this garland. Returns ErrNotSupported when emacs locks were not enabled for this garland.

func (*Garland) ByteCount

func (g *Garland) ByteCount() CountResult

ByteCount returns total bytes (or known bytes if still loading). For revisions created during streaming, includes the streaming remainder.

func (*Garland) ByteToLineRune

func (g *Garland) ByteToLineRune(bytePos int64) (line, runeInLine int64, err error)

ByteToLineRune converts a byte position to a line:rune position.

func (*Garland) ByteToRune

func (g *Garland) ByteToRune(bytePos int64) (int64, error)

ByteToRune converts a byte position to a rune position.

func (*Garland) CheckMemoryPressure

func (g *Garland) CheckMemoryPressure() MaintenanceStats

CheckMemoryPressure checks if memory limits are exceeded and performs appropriate maintenance. Called after mutations. Sets memoryPressure flag if hard limit exceeded and can't be reduced.

func (*Garland) CheckSourceMetadata

func (g *Garland) CheckSourceMetadata() (SourceChangeInfo, error)

CheckSourceMetadata performs a cheap metadata check on the source file. This only stats the file, it doesn't read any content.

func (*Garland) Checkpoint

func (g *Garland) Checkpoint() error

Checkpoint commits all active optimized regions across all cursors. This creates a single revision containing all pending region changes. Call this to establish an undo point after a burst of edits.

func (*Garland) Chill

func (g *Garland) Chill(level ChillLevel) error

Chill moves data to cold storage based on the specified aggressiveness level. This frees memory by storing data externally, to be reloaded on demand.

For MemoryOnly files, this is a no-op by design.

Levels:

  • ChillInactiveForks: Only chill data not used by the current fork
  • ChillOldHistory: Also chill old undo history beyond recent revisions
  • ChillUnusedData: Chill everything not used at current revision
  • ChillEverything: Chill all data (for switching documents or shells)

func (*Garland) Close

func (g *Garland) Close() error

Close releases resources associated with the Garland.

func (*Garland) CurrentFork

func (g *Garland) CurrentFork() ForkID

CurrentFork returns the current fork ID.

func (*Garland) CurrentRevision

func (g *Garland) CurrentRevision() RevisionID

CurrentRevision returns the current revision number within the current fork.

func (*Garland) Decorate

func (g *Garland) Decorate(entries []DecorationEntry) (ChangeResult, error)

Decorate adds, updates, or removes decorations at absolute positions. All changes are applied as a single revision. Pass nil Address in a DecorationEntry to delete that decoration.

func (*Garland) DeleteFork

func (g *Garland) DeleteFork(fork ForkID) error

DeleteFork soft-deletes a fork, preventing further navigation to it. The fork's data remains until no other forks depend on it. Cannot delete the current fork or the last remaining non-deleted fork.

func (*Garland) DisableSourceWatch

func (g *Garland) DisableSourceWatch()

DisableSourceWatch stops periodic monitoring of the source file.

func (*Garland) DumpDecorations

func (g *Garland) DumpDecorations(fs FileSystemInterface, path string) error

DumpDecorations writes all decorations to a file in INI-like format. If fs is nil, uses the Garland's source filesystem.

func (*Garland) EnableSourceWatch

func (g *Garland) EnableSourceWatch(interval time.Duration)

EnableSourceWatch starts periodic monitoring of the source file.

func (*Garland) FindForksBetween

func (g *Garland) FindForksBetween(revisionFirst, revisionLast RevisionID) ([]ForkDivergence, error)

FindForksBetween returns all fork divergence points between two revisions from the perspective of the current fork.

func (*Garland) ForceRebalance

func (g *Garland) ForceRebalance() MaintenanceStats

ForceRebalance performs a full tree rebalance (not incremental). Use sparingly as this can be expensive for large trees.

func (*Garland) ForkSeek

func (g *Garland) ForkSeek(fork ForkID) error

ForkSeek switches to a different fork. Retains current revision if it exists in both forks, otherwise retreats to the last common revision.

func (*Garland) GetDecorationPosition

func (g *Garland) GetDecorationPosition(key string) (AbsoluteAddress, error)

GetDecorationPosition returns the current position of a decoration by key. Uses registry-based O(1) existence check and cached location hints for fast lookup.

func (*Garland) GetDecorationsInByteRange

func (g *Garland) GetDecorationsInByteRange(start, end int64) ([]DecorationEntry, error)

GetDecorationsInByteRange returns all decorations within [start, end).

func (*Garland) GetDecorationsOnLine

func (g *Garland) GetDecorationsOnLine(line int64) ([]DecorationEntry, error)

GetDecorationsOnLine returns all decorations on the specified line.

func (*Garland) GetForkInfo

func (g *Garland) GetForkInfo(fork ForkID) (*ForkInfo, error)

GetForkInfo returns information about a specific fork.

func (*Garland) GetRevisionInfo

func (g *Garland) GetRevisionInfo(revision RevisionID) (*RevisionInfo, error)

GetRevisionInfo returns information about a specific revision.

func (*Garland) GetRevisionRange

func (g *Garland) GetRevisionRange(start, end RevisionID) ([]RevisionInfo, error)

GetRevisionRange returns info for revisions in [start, end] inclusive.

func (*Garland) GetSnapshotStats

func (g *Garland) GetSnapshotStats() SnapshotStats

GetSnapshotStats returns statistics about node snapshots. Useful for testing and diagnostics to verify garbage collection.

func (*Garland) GetTreeInfo

func (g *Garland) GetTreeInfo() *TreeNodeInfo

GetTreeInfo returns a snapshot of the current tree structure for visualization.

func (*Garland) HoldsSourceLock added in v0.1.7

func (g *Garland) HoldsSourceLock() bool

HoldsSourceLock reports whether this garland currently holds the emacs-style lock on its source file.

func (*Garland) InTransaction

func (g *Garland) InTransaction() bool

InTransaction returns true if any transaction is active.

func (*Garland) IncrementalRebalance

func (g *Garland) IncrementalRebalance(affectedPath []NodeID) MaintenanceStats

IncrementalRebalance performs budgeted tree rebalancing along a path. Called after mutations with the path of affected nodes.

func (*Garland) IntegrityEvents added in v0.1.4

func (g *Garland) IntegrityEvents() []IntegrityEvent

IntegrityEvents returns a copy of the integrity events accumulated since the last successful save (which drains them into its SaveReport).

func (*Garland) IsByteReady

func (g *Garland) IsByteReady(pos int64) bool

IsByteReady returns true if the given byte position is available for reading. This is a non-blocking check that can be used to guard against blocking waits. A negative position returns false. A position beyond EOF returns true only when loading is complete (at which point seeking there would return ErrInvalidPosition).

func (*Garland) IsComplete

func (g *Garland) IsComplete() bool

IsComplete returns true if EOF has been reached during loading.

func (*Garland) IsLineReady

func (g *Garland) IsLineReady(line int64) bool

IsLineReady returns true if the given line number is available for reading. This is a non-blocking check that can be used to guard against blocking waits.

func (*Garland) IsReady

func (g *Garland) IsReady() bool

IsReady returns true if initial ready threshold has been met.

func (*Garland) IsRuneReady

func (g *Garland) IsRuneReady(pos int64) bool

IsRuneReady returns true if the given rune position is available for reading. This is a non-blocking check that can be used to guard against blocking waits.

func (*Garland) LastSave added in v0.1.7

func (g *Garland) LastSave() (SavePoint, bool)

LastSave returns the most recent save point, if any.

func (*Garland) LineCount

func (g *Garland) LineCount() CountResult

LineCount returns total newlines (or known newlines if still loading).

func (*Garland) LineRuneToByte

func (g *Garland) LineRuneToByte(line, runeInLine int64) (int64, error)

LineRuneToByte converts a line:rune position to a byte position.

func (*Garland) ListForks

func (g *Garland) ListForks() []ForkInfo

ListForks returns information about all forks.

func (*Garland) LoadAppendedContent

func (g *Garland) LoadAppendedContent() (int64, error)

LoadAppendedContent loads newly appended content from the source file. Only valid after VerifyBoundaryForAppend succeeds.

func (*Garland) LoadDecorations

func (g *Garland) LoadDecorations(fs FileSystemInterface, path string) error

LoadDecorations loads decorations from a file using the INI format. Unknown sections are ignored for future compatibility. Comments are lines starting with ';' or '# ' (hash followed by space). End-of-line comments start with whitespace followed by ';' or '#'.

func (*Garland) LoadDecorationsFromString

func (g *Garland) LoadDecorationsFromString(content string) error

LoadDecorationsFromString loads decorations from an INI-formatted string. Unknown sections are ignored for future compatibility. Comments are lines starting with ';' or '# ' (hash followed by space). End-of-line comments start with whitespace followed by ';' or '#'.

func (*Garland) MemoryPressure added in v0.1.4

func (g *Garland) MemoryPressure() MemoryPressureInfo

MemoryPressure reports the buffer's memory standing. See MemoryPressureInfo for the intended app-side use.

func (*Garland) MemoryUsage

func (g *Garland) MemoryUsage() MemoryStats

MemoryUsage returns current memory statistics for this Garland.

func (*Garland) NeedsRebalancing

func (g *Garland) NeedsRebalancing() bool

NeedsRebalancing checks if the tree is significantly unbalanced.

func (*Garland) NewCursor

func (g *Garland) NewCursor() *Cursor

NewCursor creates a new cursor at position 0. Its position is tracked through undo/redo/fork navigation (see Cursor.SetTracksHistory).

func (*Garland) NewEphemeralCursor added in v0.1.5

func (g *Garland) NewEphemeralCursor() *Cursor

NewEphemeralCursor creates a cursor whose position is NOT recorded per revision or restored on seeks - for paint carets, maintenance scans, and transient work that only needs a live, edit-adjusted position. Equivalent to NewCursor followed by SetTracksHistory(false), but without ever allocating the initial history entry.

func (*Garland) NodeManipulations added in v0.1.1

func (g *Garland) NodeManipulations() int64

NodeManipulations returns the count of node operations since the last rebalance. This is a cheap counter incremented during tree mutations.

func (*Garland) Prune

func (g *Garland) Prune(keepFromRevision RevisionID) error

Prune removes revision history before keepFromRevision in the current fork. Revisions >= keepFromRevision are kept. This sets the fork's PrunedUpTo watermark and cleans up: - RevisionInfo entries for pruned revisions - Cursor position history for pruned revisions - Node snapshots that are no longer needed by any fork

Shared revisions (inherited from parent forks) are only truly deleted when all forks that share them have pruned past that point.

func (*Garland) RebaseOnFile added in v0.1.4

func (g *Garland) RebaseOnFile(fs FileSystemInterface, name string) (RebaseReport, error)

RebaseOnFile reconciles the buffer against the named file, which then becomes the buffer's source (path, handle, and warm backing switch to it). A nil fs uses the library default.

func (*Garland) RebaseOnSource added in v0.1.4

func (g *Garland) RebaseOnSource() (RebaseReport, error)

RebaseOnSource reconciles the buffer against its own source file. See the file header for semantics.

func (*Garland) RefreshSourceInfo

func (g *Garland) RefreshSourceInfo() error

RefreshSourceInfo updates stored metadata after a save.

func (*Garland) RemoveCursor

func (g *Garland) RemoveCursor(c *Cursor) error

RemoveCursor removes a cursor from the Garland.

func (*Garland) ReportSourceMetadata added in v0.1.7

func (g *Garland) ReportSourceMetadata(meta FileMetadata) SourceConsistencyState

ReportSourceMetadata volunteers fresh metadata for the source file - the push-style counterpart to SourceConsistency's pull. Use it when the app learns facts Garland cannot observe itself (its own file watcher, a sync client, a virtualized filesystem event). The observation is recorded exactly as a stat result would be, and the resulting consistency state is returned. When no baseline exists yet (a metadata-less filesystem hook), the first volunteered observation becomes the baseline.

func (*Garland) RevertToLastSave added in v0.1.7

func (g *Garland) RevertToLastSave() error

RevertToLastSave rewinds the buffer to the state captured by the most recent save - "revert to last saved version" as a pure history seek. Nothing is destroyed: the abandoned edits remain reachable as redo / fork history until the app prunes them. Returns ErrRevisionNotFound when no save was recorded (or the revision was pruned away).

func (*Garland) RuneCount

func (g *Garland) RuneCount() CountResult

RuneCount returns total runes (or known runes if still loading).

func (*Garland) RuneToByte

func (g *Garland) RuneToByte(runePos int64) (int64, error)

RuneToByte converts a rune position to a byte position.

func (*Garland) Save

func (g *Garland) Save() (SaveReport, error)

Save overwrites the original file IN PLACE with the current content. The file is rewritten without a temporary copy (peak disk usage never exceeds max(old, new) size) and shrinks only as the final step; warm storage survives the save, re-homed to the new layout. Undo history that depends on overwritten warm bytes is migrated to cold storage first. See save.go for the full design. The returned SaveReport lists any blocks whose data was lost and had to be written as visible scars; the app decides how to surface them. Returns ErrNoDataSource if there is no original file path.

func (*Garland) SaveAs

func (g *Garland) SaveAs(fs FileSystemInterface, name string) (SaveReport, error)

SaveAs writes the current content to a new location. Warm storage remains pointing to the original file (if any). Saving onto the original file itself routes through the in-place engine so the warm backing store is never destroyed. The returned SaveReport lists any lost blocks written as scars. Equivalent to SaveAsWith(fs, name, SaveAsOptions{}) - the destination does NOT become the buffer's source.

func (*Garland) SaveAsWith added in v0.1.7

func (g *Garland) SaveAsWith(fs FileSystemInterface, name string, opts SaveAsOptions) (SaveReport, error)

SaveAsWith writes the current content to a new location with control over whether the destination becomes the buffer's new source (warm- storage location). See SaveAsOptions. The returned SaveReport lists any lost blocks written as scars.

func (*Garland) SaveHistory added in v0.1.7

func (g *Garland) SaveHistory() []SavePoint

SaveHistory returns the recorded save points, oldest first (bounded to the most recent few).

func (*Garland) SaveWith added in v0.1.4

func (g *Garland) SaveWith(opts SaveOptions) (SaveReport, error)

SaveWith overwrites the original file in place with the current content. See the file header for the full design. The report lists any lost blocks that were scarred; the app should warn the user.

func (*Garland) SetAppendPolicy

func (g *Garland) SetAppendPolicy(policy AppendPolicy)

SetAppendPolicy sets the policy for handling detected appends.

func (*Garland) SetBackupLocation added in v0.1.7

func (g *Garland) SetBackupLocation(fs FileSystemInterface, dir string, opts BackupOptions) error

SetBackupLocation configures (or, with an empty dir, disables) pre-session backups for this garland. The directory may live on any filesystem (nil fs = library default / local disk). Configuring replaces any previous configuration; an uncommitted previous backup is removed. If the buffer already holds unsaved modifications, the capture arms immediately (the source file still holds the pre-session content - nothing has overwritten it yet). Returns ErrNoDataSource when the buffer has no source file (there is nothing on disk to protect).

func (*Garland) SetSourceChangeHandler

func (g *Garland) SetSourceChangeHandler(handler SourceChangeHandler)

SetSourceChangeHandler sets a callback for source file changes.

func (*Garland) SetUndoCoalescing added in v0.1.8

func (g *Garland) SetUndoCoalescing(enabled bool, autoBakeTime time.Duration)

SetUndoCoalescing enables or disables undo coalescing and sets the auto-bake window. While enabled, runs of adjacent inserts (typing), adjacent deletes (backspacing / forward-deleting at one caret), and adjacent overwrites (replace-mode typing) each collapse into a single revision - the three kinds run separately and never merge with one another. autoBakeTime > 0 bakes the run automatically when the next edit arrives more than that long after the previous one; 0 disables time-based baking. Changing the configuration is itself a hard edge. Disabled by default - every mutation is its own revision unless this is turned on.

func (*Garland) SetVerifyOnRead

func (g *Garland) SetVerifyOnRead(enabled bool)

SetVerifyOnRead sets whether warm storage reads should verify checksums.

func (*Garland) SourceConsistency added in v0.1.7

func (g *Garland) SourceConsistency() (SourceConsistencyReport, error)

SourceConsistency performs a fresh metadata check through the filesystem hook and reports the source file's consistency state. With a metadata-less hook it reports from the last volunteered observation instead (never an error - the report just says what is actually known). When emacs locks are enabled the lock file is also re-probed, so a foreign editor grabbing the file shows up here.

func (*Garland) SourceConsistencyCached added in v0.1.7

func (g *Garland) SourceConsistencyCached() SourceConsistencyReport

SourceConsistencyCached reports the consistency state from what Garland already knows, with NO filesystem access - safe to call from a paint path. Pair it with volunteered metadata (ReportSourceMetadata) or periodic SourceConsistency / EnableSourceWatch calls.

func (*Garland) SourceDeviceInfo added in v0.1.7

func (g *Garland) SourceDeviceInfo() (DeviceInfo, error)

SourceDeviceInfo reports the storage device behind this buffer's source file. Returns ErrNoDataSource when there is no source path.

func (*Garland) SourceLockOwner added in v0.1.7

func (g *Garland) SourceLockOwner() (string, bool)

SourceLockOwner reports the owner string of a foreign emacs-style lock on the source file ("user@host.pid"), as last observed. Returns ("", false) when no foreign lock is known - including when locks are disabled. SourceConsistency re-probes the lock file; this call does not touch the filesystem.

func (*Garland) SourcePath

func (g *Garland) SourcePath() string

SourcePath returns the path to the source file, if any.

func (*Garland) SourceStatus

func (g *Garland) SourceStatus() SourceChangeStatus

SourceStatus returns the current source change status.

func (*Garland) Thaw

func (g *Garland) Thaw() error

Thaw restores data from cold storage to memory for the current fork. This is the inverse of Chill - it loads data back from cold storage.

func (*Garland) ThawRange

func (g *Garland) ThawRange(startByte, endByte int64) error

ThawRange restores cold data for a specific byte range at the current revision. This is RAM-safe for large files - it only thaws the nodes needed to read the specified byte range instead of the entire file.

func (*Garland) ThawRevision

func (g *Garland) ThawRevision(startRev, endRev RevisionID) error

ThawRevision restores cold data for a specific revision range in the current fork. WARNING: This thaws ALL data for the revision(s), which could be very large. For large files, prefer ThawRange() to thaw only the bytes you need.

func (*Garland) TransactionCommit

func (g *Garland) TransactionCommit() (ChangeResult, error)

TransactionCommit commits the current transaction.

func (*Garland) TransactionDepth

func (g *Garland) TransactionDepth() int

TransactionDepth returns the current nesting depth (0 = no active transaction).

func (*Garland) TransactionRollback

func (g *Garland) TransactionRollback() error

TransactionRollback discards all changes in the current transaction.

func (*Garland) TransactionStart

func (g *Garland) TransactionStart(name string) error

TransactionStart begins a new transaction with an optional descriptive name.

func (*Garland) TryRecoverSource added in v0.1.7

func (g *Garland) TryRecoverSource(level VerifyLevel) (SavePoint, error)

TryRecoverSource walks the save history newest-first looking for an alternate known location whose file can be adopted as the source - automatic recovery for when the current source becomes corrupt or unreachable. Each candidate is checked at the given level (see AdoptWarmSource); the first that verifies is adopted and its save point returned. Returns ErrNoRecoverySource when no alternate location works.

func (*Garland) UndoCoalescing added in v0.1.8

func (g *Garland) UndoCoalescing() (enabled bool, autoBakeTime time.Duration)

UndoCoalescing reports the current coalescing configuration.

func (*Garland) UndoSeek

func (g *Garland) UndoSeek(revision RevisionID) error

UndoSeek navigates to a specific revision within the current fork. Cannot seek forward past the highest revision in this fork. Seeking backwards then making a change creates a new fork.

func (*Garland) VerifyBoundaryForAppend

func (g *Garland) VerifyBoundaryForAppend() error

VerifyBoundaryForAppend verifies just the boundary block to confirm an append. Returns nil if the boundary is intact (safe to treat as append).

type IntegrityEvent added in v0.1.4

type IntegrityEvent struct {
	Kind         IntegrityKind
	BufferOffset int64  // block's byte offset in the buffer at discovery (-1 if unknown)
	FileOffset   int64  // where its backing bytes lived in the source file (-1 if none)
	Length       int64  // byte count of the block
	Detail       string // human-readable specifics (shift distance, duplicate location, cause)
}

IntegrityEvent records one block-level integrity finding, kept from the moment of discovery until it is reported to the app (peek with Garland.IntegrityEvents; a successful save drains pending events into its SaveReport).

type IntegrityKind added in v0.1.4

type IntegrityKind int

IntegrityKind classifies what happened to a block whose backing bytes stopped matching expectations.

const (
	// IntegrityBlockSlid: the block was found intact at a shifted file
	// offset (external insert/delete earlier in the file) and was
	// re-homed. Nothing lost; buffer content unchanged.
	IntegrityBlockSlid IntegrityKind = iota

	// IntegrityBlockSwapped: the block's exact bytes were found at
	// another block's backing position, and that block's bytes at
	// ours - an external move. Backing offsets were exchanged.
	// Nothing lost; buffer content unchanged.
	IntegrityBlockSwapped

	// IntegrityBlockAdopted: soft loss of integrity. The surrounding
	// blocks still verify, so the mismatch looks like a deliberate
	// external edit confined to this block. The file's bytes were
	// adopted into the buffer (a future save preserves them); the
	// buffer's previous bytes for this block are gone.
	IntegrityBlockAdopted

	// IntegrityBlockAdoptedDuplicate: adopted as above, but the
	// adopted content is an exact duplicate of another preserved
	// block - an external move/copy is likely, so the app may want to
	// look more closely.
	IntegrityBlockAdoptedDuplicate

	// IntegrityBlockLost: hard corruption. Nothing around the block
	// matched expectations and no relocation was found. The block is a
	// placeholder and will be written as a scar by the next save.
	IntegrityBlockLost

	// IntegrityDecorationsLost: a block's CONTENT thawed fine but its
	// decorations could not be restored from cold storage (side block
	// missing, hash mismatch, or corrupt encoding). The marks in that
	// block are gone; everything else is intact.
	IntegrityDecorationsLost

	// IntegrityBlockResized: an external edit INSIDE this block changed
	// its length - its old bytes no longer exist contiguously anywhere,
	// so reads fail (placeholder), but the situation is precisely
	// diagnosed: the neighbor before verifies at its recorded offset
	// and the neighbor after verifies shifted by the file's size
	// change. RebaseOnSource() will adopt the resized content cleanly.
	IntegrityBlockResized
)

func (IntegrityKind) String added in v0.1.4

func (k IntegrityKind) String() string

String returns a short human-readable name for the kind.

type LeafSearchResult

type LeafSearchResult struct {
	Node                  *Node         // the leaf node
	Snapshot              *NodeSnapshot // the node's snapshot at current version
	ByteOffset            int64         // byte offset from start of this leaf to target
	RuneOffset            int64         // rune offset from start of this leaf to target
	LeafByteStart         int64         // absolute byte position where this leaf starts
	LeafRuneStart         int64         // absolute rune position where this leaf starts
	RunesOnLineBeforeLeaf int64         // runes on current line before this leaf starts
}

LeafSearchResult contains information about a leaf node found during tree traversal.

type Library

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

Library manages garland instances and shared resources like cold storage.

func Init

func Init(options LibraryOptions) (*Library, error)

Init initializes the garland library with cold storage options. Cold storage is shared across all files opened through this library instance.

func (*Library) CheckMemoryPressureError

func (lib *Library) CheckMemoryPressureError() error

CheckMemoryPressureError returns ErrMemoryPressure if the library is under memory pressure, nil otherwise. Useful for checking before mutations.

func (*Library) ChillToTarget

func (lib *Library) ChillToTarget() MaintenanceStats

ChillToTarget performs incremental chilling until memory is below the soft limit. It respects the budget per tick and returns when either: - Memory is below soft limit - No more candidates to chill - Budget exhausted for this tick

func (*Library) Close

func (lib *Library) Close() error

Close properly shuts down a Garland, including stopping maintenance.

func (*Library) DefaultFS added in v0.1.6

func (lib *Library) DefaultFS() FileSystemInterface

DefaultFS returns the filesystem this library uses for local-disk operations (the same one Garland resolves to when no explicit filesystem is given). Useful for hosts that want to wrap or delegate to it when composing a custom FileSystemInterface.

func (*Library) DeviceInfoFor added in v0.1.7

func (lib *Library) DeviceInfoFor(fs FileSystemInterface, name string) (DeviceInfo, error)

DeviceInfoFor reports the storage device behind a path (identity and free space) through the given filesystem, for free-space warnings before a save. A nil fs uses the library default (local disk).

func (*Library) IncrementalChill

func (lib *Library) IncrementalChill(budget int) MaintenanceStats

IncrementalChill performs budgeted LRU-based chilling across all Garlands. It chills at most `budget` nodes, prioritizing least-recently-used. Returns the number of nodes chilled and bytes freed.

func (*Library) MemoryPressure

func (lib *Library) MemoryPressure() bool

MemoryPressure returns true if the library is under memory pressure (hard limit exceeded and cannot be reduced). Applications should check this before performing memory-intensive operations.

func (*Library) Open

func (lib *Library) Open(options FileOptions) (*Garland, error)

Open creates or loads a Garland from various sources.

func (*Library) StopMaintenance

func (lib *Library) StopMaintenance()

StopMaintenance stops the background maintenance worker.

func (*Library) TotalMemoryUsage

func (lib *Library) TotalMemoryUsage() int64

TotalMemoryUsage returns the total memory usage across all Garlands in the library.

type LibraryOptions

type LibraryOptions struct {
	// ColdStoragePath is a filesystem path for cold storage.
	// Either this or ColdStorageBackend must be provided (or both).
	ColdStoragePath string

	// ColdStorageBackend is a custom cold storage implementation.
	ColdStorageBackend ColdStorageInterface

	// Memory management options
	// MemorySoftLimit is the target memory usage in bytes.
	// When exceeded, background maintenance starts chilling LRU nodes.
	// 0 means disabled (default).
	MemorySoftLimit int64

	// MemoryHardLimit is the maximum memory usage in bytes.
	// When exceeded, immediate (but still budgeted) chilling occurs.
	// 0 means disabled (default).
	MemoryHardLimit int64

	// ChillBudgetPerTick is the maximum nodes to chill per maintenance tick.
	// Default is 5 if not specified.
	ChillBudgetPerTick int

	// RebalanceBudget is the maximum rotations per mutation operation.
	// Default is 2 if not specified.
	RebalanceBudget int

	// BackgroundInterval is how often the background maintenance worker runs.
	// 0 means disabled (maintenance only happens opportunistically).
	// Typical value: 100ms to 1s.
	BackgroundInterval time.Duration
}

LibraryOptions configures the garland library.

type LineSearchResult

type LineSearchResult struct {
	LeafResult    *LeafSearchResult
	LineByteStart int64 // absolute byte position where target line starts
	LineRuneStart int64 // absolute rune position where target line starts
}

LineSearchResult contains information about a line found during tree traversal.

type LineStart

type LineStart struct {
	// ByteOffset is the byte offset from the start of the node where this line begins.
	ByteOffset int64

	// RuneOffset is the rune offset from the start of the node where this line begins.
	RuneOffset int64
}

LineStart represents the starting position of a line within a leaf node.

type Loader

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

Loader handles background loading of data from various sources.

type LoadingStyle

type LoadingStyle int

LoadingStyle determines which storage tiers are available.

const (
	// AllStorage allows memory, warm (original file), and cold storage.
	// Warm storage requires the original file to be unchanged.
	AllStorage LoadingStyle = iota

	// ColdAndMemory prevents warm storage, only memory and cold.
	ColdAndMemory

	// MemoryOnly keeps everything in memory, no external storage.
	MemoryOnly
)

type MaintenanceStats

type MaintenanceStats struct {
	NodesChilled       int   // number of nodes moved to cold storage
	BytesChilled       int64 // bytes moved to cold storage
	RotationsPerformed int   // number of tree rotations performed
}

MaintenanceStats contains statistics from a maintenance run.

type MemoryPressureInfo added in v0.1.4

type MemoryPressureInfo struct {
	// ResidentBytes is the leaf data currently held in memory.
	ResidentBytes int64
	// EvictableBytes could be chilled to warm or cold storage right
	// now without any save.
	EvictableBytes int64
	// SaveableBytes are current-revision bytes that only a Save can
	// make evictable: they have no file backing yet and no cold
	// backend exists, so saving (which gives them warm backing) is
	// the only way to get them out of memory.
	SaveableBytes int64
	// SoftLimitBytes / HardLimitBytes echo the configured library
	// limits (0 = none).
	SoftLimitBytes int64
	HardLimitBytes int64
}

MemoryPressureInfo describes where this buffer's resident memory stands and what can relieve it. It is the signal for an app-side "hot-write mode": when SaveableBytes dominates ResidentBytes and the total approaches the hard limit, saving is the only relief left - "in order to keep editing, I need to save before I run out of RAM." The library reports; the app decides when to prompt or act.

type MemoryStats

type MemoryStats struct {
	MemoryBytes      int64 // bytes of in-memory leaf data
	SoftLimit        int64 // configured soft limit (0 = disabled)
	HardLimit        int64 // configured hard limit (0 = disabled)
	InMemoryLeaves   int   // count of leaves with data in memory
	ColdStoredLeaves int   // count of leaves with data in cold storage
	WarmStoredLeaves int   // count of leaves with data in warm storage
	UnderPressure    bool  // true if hard limit exceeded and can't reduce
}

MemoryStats contains current memory usage statistics.

type MoveResult

type MoveResult struct {
	ChangeResult
	DisplacedDecorations []RelativeDecoration // Decorations that were in the destination range (original positions)
}

MoveResult contains the result of a Move operation.

type Node

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

Node is a versioned container in the rope structure. Each node maintains a history of its state indexed by (ForkID, RevisionID).

func (*Node) ID

func (n *Node) ID() NodeID

ID returns the node's unique identifier.

type NodeID

type NodeID uint64

NodeID uniquely identifies a node within a Garland.

type NodeSnapshot

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

NodeSnapshot represents the immutable state of a node at a specific version.

func (*NodeSnapshot) ByteCount

func (s *NodeSnapshot) ByteCount() int64

ByteCount returns the total bytes in this subtree.

func (*NodeSnapshot) Data

func (s *NodeSnapshot) Data() []byte

Data returns the leaf node's data. Returns nil for internal nodes.

func (*NodeSnapshot) Decorations

func (s *NodeSnapshot) Decorations() []Decoration

Decorations returns the leaf node's decorations. Returns nil for internal nodes.

func (*NodeSnapshot) IsLeaf

func (s *NodeSnapshot) IsLeaf() bool

IsLeaf returns true if this snapshot represents a leaf node.

func (*NodeSnapshot) LeftID

func (s *NodeSnapshot) LeftID() NodeID

LeftID returns the left child's ID. Only valid for internal nodes.

func (*NodeSnapshot) LineCount

func (s *NodeSnapshot) LineCount() int64

LineCount returns the total newlines in this subtree.

func (*NodeSnapshot) RightID

func (s *NodeSnapshot) RightID() NodeID

RightID returns the right child's ID. Only valid for internal nodes.

func (*NodeSnapshot) RuneCount

func (s *NodeSnapshot) RuneCount() int64

RuneCount returns the total runes in this subtree.

type OpenMode

type OpenMode int

OpenMode specifies how a file should be opened.

const (
	// OpenModeRead opens the file for reading only.
	OpenModeRead OpenMode = iota

	// OpenModeWrite opens the file for writing only.
	OpenModeWrite

	// OpenModeReadWrite opens the file for reading and writing.
	OpenModeReadWrite
)

type OptimizedRegionHandle

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

OptimizedRegionHandle tracks an active optimized region attached to a cursor.

func (*OptimizedRegionHandle) ByteCount

func (h *OptimizedRegionHandle) ByteCount() int64

ByteCount returns the current byte count of the region content.

func (*OptimizedRegionHandle) ContentBounds

func (h *OptimizedRegionHandle) ContentBounds() (start, end int64)

ContentBounds returns the current content bounds (document coordinates).

func (*OptimizedRegionHandle) GraceWindow

func (h *OptimizedRegionHandle) GraceWindow() (start, end int64)

GraceWindow returns the grace window bounds (document coordinates).

func (*OptimizedRegionHandle) Serial

func (h *OptimizedRegionHandle) Serial() uint64

Serial returns the unique serial number of this region for debugging.

type ReadAheadConfig

type ReadAheadConfig struct {
	Lines int64 // additional lines to read ahead (0 = ignore)
	Bytes int64 // additional bytes to read ahead (0 = ignore)
	Runes int64 // additional runes to read ahead (0 = ignore)
	All   bool  // read entire file ASAP
}

ReadAheadConfig specifies lazy read-ahead behavior.

type ReadyThreshold

type ReadyThreshold struct {
	Lines int64 // number of complete lines (0 = ignore)
	Bytes int64 // number of bytes (0 = ignore)
	Runes int64 // number of runes (0 = ignore)
	All   bool  // only ready when entire file processed
}

ReadyThreshold specifies when a garland is considered "ready".

type RebaseRegion added in v0.1.4

type RebaseRegion struct {
	Offset int64 // byte offset in the new buffer (== file offset)
	Length int64
}

RebaseRegion is one contiguous region of the new buffer whose content came from the file rather than from preserved blocks.

type RebaseReport added in v0.1.4

type RebaseReport struct {
	// Adopted lists the regions whose content was taken from the file
	// (external edits ingested; unsaved local edits displaced).
	Adopted []RebaseRegion

	BytesKept    int64 // bytes preserved through matched blocks
	BytesAdopted int64 // bytes taken from the file
	BlocksKept   int   // matched blocks (identity preserved)
	BlocksHealed int   // placeholder blocks recovered from the file

	OldSize int64 // buffer size before the rebase
	NewSize int64 // buffer size after (== file size)

	// NoChange is true when the buffer already matched the file
	// byte-for-byte: no mutation was recorded (backing offsets may
	// still have been re-homed and placeholders healed in place).
	NoChange bool

	// PreviousRevision is the revision holding the pre-rebase buffer;
	// UndoSeek there is the "keep your version" escape hatch. Equal to
	// the current revision when NoChange.
	PreviousRevision RevisionID
}

RebaseReport describes what a Rebase did.

type RegexOptions

type RegexOptions struct {
	CaseInsensitive bool // If true, regex is case-insensitive
	Backward        bool // If true, search backward from cursor
}

RegexOptions configures regex search behavior.

type RelativeDecoration

type RelativeDecoration struct {
	Key      string
	Position int64
}

RelativeDecoration specifies a decoration position relative to an insert point. Position semantics:

  • < 0 : attach after the newline preceding the insert point
  • 0 to Len : attach to the byte/rune at that offset in inserted content
  • Len + 1 : attach before the newline following the insert point
  • > Len + 1: attach after the newline following the insert point

type RevisionID

type RevisionID uint64

RevisionID identifies a revision within a fork.

type RevisionInfo

type RevisionInfo struct {
	Revision         RevisionID
	Name             string // from TransactionStart
	HasChanges       bool   // true if actual mutations occurred
	RootID           NodeID // root node ID at this revision (for UndoSeek)
	StreamKnownBytes int64  // bytes of streaming content known when revision was created (-1 if complete)
}

RevisionInfo contains metadata about a revision for undo history display.

type SaveAsOptions added in v0.1.7

type SaveAsOptions struct {
	// AdoptAsSource makes the destination the buffer's new source and
	// warm-storage backing after a successful write: warm references
	// re-home onto the new file, change detection re-baselines against
	// it, and future Save calls write there. Use this for "the file
	// lives here now". Leave it false when the destination must not be
	// depended on afterwards - an export, or removable media about to
	// be ejected - so the buffer keeps working from its original
	// source.
	AdoptAsSource bool

	// PreserveHistory (adoption only): undo history backed by the OLD
	// source is migrated off it (cold storage when available, else
	// memory) before the source switches. When false such history is
	// marked lost - amputated, never silently corrupted. Ignored when
	// AdoptAsSource is false (the old source stays attached, history
	// untouched).
	PreserveHistory bool
}

SaveAsOptions configures SaveAsWith.

type SaveOptions added in v0.1.4

type SaveOptions struct {
	// PreserveHistory protects warm-backed data that only OLDER
	// revisions reference: before the rewrite can overwrite its
	// backing bytes it is migrated to cold storage (or held in memory
	// when no cold backend is configured), so undo history survives
	// the save intact.
	//
	// When false, such history keeps pointing at its old file offsets;
	// regions the rewrite left untouched remain valid, and regions
	// that were overwritten fail their hash check on access and become
	// placeholders - undo history may be amputated, but never silently
	// corrupted.
	PreserveHistory bool

	// Concurrent (opt-in) runs the rewrite WITHOUT holding the buffer
	// lock: the app may keep reading and editing on its op goroutine
	// while the save writes (only Prune, DeleteFork, Rebase, Close,
	// and other saves wait for it). The cost is that every warm span
	// the rewrite displaces must first be EVACUATED - to cold storage
	// when a backend exists, else into memory - because the file
	// cannot be both the old layout (for live warm reads) and the new
	// layout (being written) at once. When there is no cold backend
	// and the evacuation would push memory past the configured hard
	// limit, the save transparently falls back to the locked zero-copy
	// path (SaveReport.Concurrent reports what actually ran).
	Concurrent bool
}

SaveOptions configures Save behavior.

type SavePoint added in v0.1.7

type SavePoint struct {
	// Fork and Revision identify the buffer state the save wrote.
	Fork     ForkID
	Revision RevisionID

	// Path is where the content was written.
	Path string

	// Meta is the file's metadata observed right after the save (zero
	// when the destination filesystem cannot report metadata).
	Meta FileMetadata

	// SavedAt is when the save completed.
	SavedAt time.Time

	// AdoptedAsSource reports whether this location became (or already
	// was) the buffer's source / warm-storage backing at save time.
	AdoptedAsSource bool
	// contains filtered or unexported fields
}

SavePoint records one successful save: where the content went, what the file looked like immediately afterwards, and which fork/revision the save captured - the anchor for "revert to last saved version" (a pure history seek) and for source recovery/adoption.

type SaveReport added in v0.1.4

type SaveReport struct {
	// Scars lists blocks whose data was lost to storage failure and
	// were written as visible scars instead. Empty on a clean save.
	Scars []ScarWarning

	// Integrity lists block-level integrity events discovered since
	// the last successful save: external edits that slid, moved, or
	// modified warm-backed blocks (recovered or adopted without loss),
	// and hard losses. A successful save drains the pending log here;
	// IntegrityEvents() peeks at it between saves.
	Integrity []IntegrityEvent

	// Concurrent reports whether the save actually ran without holding
	// the buffer lock. A SaveOptions.Concurrent request falls back to
	// the locked zero-copy path (Concurrent=false here) when the
	// required evacuation cannot be afforded.
	Concurrent bool
}

SaveReport carries non-fatal outcomes of a save.

type ScarWarning added in v0.1.4

type ScarWarning struct {
	Offset   int64  // byte offset of the scarred block in the saved content
	Length   int64  // byte count of the lost block (the scar occupies exactly this)
	Marker   string // the human-readable marker text
	Appended bool   // marker did not fit in the block; it was appended at EOF

	// Reason is why the data was lost, recorded at the moment the loss
	// was discovered (e.g. "cold storage read failed: ...", "source
	// file changed on disk (hash mismatch)"). Empty when the cause was
	// never observed by the library.
	Reason string
}

ScarWarning reports one lost block that was written as a visible scar during a save. The app should surface these to the user: the save succeeded, but this data is gone.

type SearchOptions

type SearchOptions struct {
	CaseSensitive bool // If false, search is case-insensitive
	WholeWord     bool // If true, only match whole words
	Backward      bool // If true, search backward from cursor
}

SearchOptions configures string search behavior.

type SearchResult

type SearchResult struct {
	ByteStart int64  // Start position in bytes
	ByteEnd   int64  // End position in bytes (exclusive)
	Match     string // The matched text
}

SearchResult contains information about a search match.

type SnapshotStats

type SnapshotStats struct {
	TotalSnapshots int                  // Total snapshots across all nodes
	ByFork         map[ForkID]int       // Snapshots per fork
	ByForkRevision map[ForkRevision]int // Snapshots per fork/revision
}

SnapshotStats contains statistics about node snapshots.

type SourceChangeHandler

type SourceChangeHandler func(g *Garland, status SourceChangeStatus, info SourceChangeInfo)

SourceChangeHandler is called when a source file change is detected.

type SourceChangeInfo

type SourceChangeInfo struct {
	Type          SourceChangeType
	PreviousSize  int64
	CurrentSize   int64
	AppendedBytes int64 // Only valid if Type == SourceAppended
}

SourceChangeInfo contains details about a detected source file change.

type SourceChangeStatus

type SourceChangeStatus int

SourceChangeStatus indicates the current status of source file tracking.

const (
	// SourceStatusNormal indicates no changes have been detected.
	SourceStatusNormal SourceChangeStatus = iota

	// SourceStatusAppendAvailable indicates an append was detected and verified.
	SourceStatusAppendAvailable

	// SourceStatusSuspectChange indicates metadata changed but not yet verified.
	SourceStatusSuspectChange

	// SourceStatusModified indicates a checksum mismatch was confirmed.
	SourceStatusModified
)

type SourceChangeType

type SourceChangeType int

SourceChangeType indicates the type of change detected in the source file.

const (
	// SourceUnchanged indicates no change was detected.
	SourceUnchanged SourceChangeType = iota

	// SourceAppended indicates the file grew but existing content is intact.
	SourceAppended

	// SourceModified indicates existing content was altered.
	SourceModified

	// SourceTruncated indicates the file was shortened.
	SourceTruncated

	// SourceReplaced indicates the file was replaced (different inode).
	SourceReplaced

	// SourceDeleted indicates the file no longer exists.
	SourceDeleted
)

func (SourceChangeType) String

func (t SourceChangeType) String() string

String returns a human-readable description of the change type.

type SourceConsistencyReport added in v0.1.7

type SourceConsistencyReport struct {
	State SourceConsistencyState

	// Baseline is the metadata recorded when buffer and file last
	// agreed (open, save, adoption). Zero when State is Untracked.
	Baseline FileMetadata

	// Observed is the most recent observation (stat through the hook or
	// volunteered), Zero when nothing was ever observed past baseline.
	Observed FileMetadata

	// ObservedAt is when Observed was recorded (zero: never).
	ObservedAt time.Time

	// LockedBy is the owner string of a foreign emacs-style lock file
	// seen on the source ("user@host.pid"), empty when none is known.
	// Only populated when FileOptions.UseEmacsLocks was enabled.
	LockedBy string
}

SourceConsistencyReport answers "can I trust the source file right now?" with everything the app needs for its UI decision.

type SourceConsistencyState added in v0.1.7

type SourceConsistencyState int

SourceConsistencyState summarizes the relationship between the buffer's baseline (the file as last known to agree with the buffer's origin) and the most recent observation of the file.

const (
	// ConsistencyUntracked: no metadata baseline exists - there is no
	// source path, or the filesystem hook does not support Stat and the
	// application never volunteered metadata.
	ConsistencyUntracked SourceConsistencyState = iota

	// ConsistencyClean: the file looks exactly as baselined.
	ConsistencyClean

	// ConsistencyAppended: the file grew; existing content may be intact
	// (verify with VerifyBoundaryForAppend).
	ConsistencyAppended

	// ConsistencyModified: same size but the modification time changed.
	ConsistencyModified

	// ConsistencyTruncated: the file shrank.
	ConsistencyTruncated

	// ConsistencyReplaced: the path is bound to a different storage
	// object (the file was replaced by rename).
	ConsistencyReplaced

	// ConsistencyMissing: the file no longer exists.
	ConsistencyMissing
)

func (SourceConsistencyState) String added in v0.1.7

func (s SourceConsistencyState) String() string

String returns a human-readable description of the state.

type StorageState

type StorageState int

StorageState indicates where a node's data is currently stored.

const (
	// StorageMemory indicates data is in memory.
	StorageMemory StorageState = iota

	// StorageWarm indicates data is in the original file (warm storage).
	StorageWarm

	// StorageCold indicates data is in cold storage.
	StorageCold

	// StoragePlaceholder indicates data was lost due to storage failure.
	StoragePlaceholder
)

type TransactionState

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

TransactionState holds the state of an active transaction.

type TreeNodeInfo

type TreeNodeInfo struct {
	NodeID       NodeID
	IsLeaf       bool
	ByteCount    int64
	RuneCount    int64
	LineCount    int64
	Storage      StorageState
	DataPreview  string // First 32 chars of leaf data (for leaves only)
	LeftChildID  NodeID // For internal nodes
	RightChildID NodeID // For internal nodes
	Children     []*TreeNodeInfo
}

TreeNodeInfo contains information about a single node in the tree.

type VerifyLevel added in v0.1.7

type VerifyLevel int

VerifyLevel selects how much evidence AdoptWarmSource requires that a candidate file's content matches the buffer, trading speed for proof.

const (
	// VerifyMetadata is the swiftest check: the candidate must carry
	// the exact metadata recorded by a save of THIS buffer state (a
	// SavePoint at the current fork/revision for that path). No content
	// is read. With a metadata-less filesystem the save point alone is
	// accepted as the best available evidence.
	VerifyMetadata VerifyLevel = iota

	// VerifySample also hash-checks a bounded sample of content spans
	// (first, last, and a few evenly spaced blocks).
	VerifySample

	// VerifyFull hash-checks every span before adopting.
	VerifyFull
)

type WarmTrustLevel

type WarmTrustLevel int

WarmTrustLevel indicates how much we trust warm storage for a given block.

const (
	// WarmTrustFull indicates no changes ever detected, fully trusted.
	WarmTrustFull WarmTrustLevel = iota

	// WarmTrustVerified indicates changes detected but block verified since.
	WarmTrustVerified

	// WarmTrustStale indicates changes detected and block not verified.
	WarmTrustStale

	// WarmTrustSuspended indicates user notified but hasn't responded.
	WarmTrustSuspended
)

type WordStyle added in v0.1.4

type WordStyle int

WordStyle selects the word-boundary semantics for word motions.

const (
	// WordStyleSimple: a word is a run of letters/digits/underscore;
	// punctuation and whitespace are separators (never stops).
	WordStyleSimple WordStyle = iota

	// WordStyleVi: like vi's w/b - punctuation runs are words of
	// their own, so both word-character runs and punctuation runs are
	// stops; only whitespace separates.
	WordStyleVi
)

Directories

Path Synopsis
cmd
garland-bench command
garland-bench is a benchmark and stress test for the Garland library.
garland-bench is a benchmark and stress test for the Garland library.
garland-repl command

Jump to

Keyboard shortcuts

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