niceyaml

package module
v0.0.0-...-b59e95e Latest Latest
Warning

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

Go to latest
Published: Jan 30, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

README

Nice YAML!

Go Reference Go Report Card Latest tag License

Package niceyaml combines the powers of go-yaml, bubbletea, and more.

It enables friendly and predictable handling of YAML-compatible documents in your CLI or TUI applications, and includes:

We use a parse-once, style-once approach. This means your users get a snappy UI, and you get a simple API. There's no need to employ multiple lexers, or perform any ANSI manipulation!

We also provide a consistent positioning system used throughout niceyaml. It enables each cell to be individually addressed, without any complexity being introduced by the specific display mode (e.g. diffs, slices).

Features

view

Revision Diffs

revisions

Themes

themes

Validation

validate

Installation

go get jacobcolvin.com/niceyaml@latest

Usage

Core Abstractions

Package niceyaml adds a few abstractions on top of go-yaml:

  • Line - Tokens for a single line of YAML content
  • Lines - Collection of Lines representing one or more YAML documents
  • Source - Manages Lines while abstracting away go-yaml lexer/parser details

Most use cases will only need to interact with Source. It satisfies most interfaces accepted by other niceyaml utilities.

These abstractions enable straightforward iteration over arbitrary lines of tokens from one or more YAML documents, while maintaining the original token details from the lexer. It cleanly solves common problems introduced by multi-line and/or overlapping tokens in diffs, partial rendering, and/or search.

flowchart LR
    A["Input"]
    subgraph Source
        B["Lexer"] --> C["Parser"]
    end
    A --> Source
    C --> D["Decoder"]
    D --> E["Struct"]

Printing YAML with Lipgloss Styles

Printing Diffs Between YAML Revisions

Searching YAML Content

Schema Generation and Validation

Full YAML Viewport Example

See cmd/nyaml for a complete Bubble Tea application that loads, pages, searches, diffs, and validates YAML documents.

Documentation

Overview

Package niceyaml provides utilities for working with YAML documents. It is built using yaml and lipgloss.

By directly styling YAML tokens a single time, niceyaml is much more consistent, flexible, and performant, when compared to using multiple distinct styling systems.

It also provides an alternative implementation of go-yaml's source annotations for errors, as well as adapters for use with JSON schema validation.

Usage

Parse YAML into a Source, then use a Printer to render it with syntax highlighting:

source := niceyaml.NewSourceFromString(yamlContent)
printer := niceyaml.NewPrinter()
fmt.Println(printer.Print(source))

When errors occur, wrap them with source context to show users exactly where the problem is:

file, err := source.File()
if err != nil {
	// Error displays the YAML with the problematic location highlighted.
	fmt.Println(source.WrapError(err))
}

Architecture

The package centers on Source, which organizes YAML tokens from go-yaml into line.Lines.

Each line tracks its tokens plus optional metadata: annotations (error messages, diff headers), flags (inserted/deleted), and overlays (style spans for highlighting).

This line-oriented structure enables efficient rendering and precise position tracking.

Printer renders any LineIterator (including *Source) with syntax highlighting via lipgloss.

It supports customizable gutters (line numbers, diff markers), word wrapping, and annotation rendering.

Themes from jacobcolvin.com/niceyaml/style/theme provide color palettes; use theme.Charm as a sensible default.

Error wraps errors with YAML source context.

When you know the error location (via token or path), Error.Error renders surrounding lines with the error position highlighted.

Multiple nested errors appear as annotations below their respective lines, with distant errors displayed in separate hunks.

Validation Pipeline

For structured validation, Decoder iterates over documents in an *ast.File and DocumentDecoder provides the validation pipeline:

source := niceyaml.NewSourceFromString(yamlContent)
file, _ := source.File()
decoder := niceyaml.NewDecoder(file)
for _, doc := range decoder.Documents() {
	var config Config
	if err := doc.Unmarshal(&config); err != nil {
		return source.WrapError(err)
	}
}

DocumentDecoder.Unmarshal supports two validation hooks: types implementing SchemaValidator are validated against an external schema before decoding, and types implementing Validator are self-validated after decoding.

Both produce Error values with path information that Source.WrapError can annotate with source context.

Diffs

Revision chains document versions in a doubly-linked list.

Differ computes line differences using the diff package. The default diff.Hirschberg algorithm is space-efficient for large files:

revs := niceyaml.NewRevision(original).Append(modified)
result := niceyaml.Diff(revs.Origin(), revs.Tip())
printer := niceyaml.NewPrinter()
fmt.Println(printer.Print(result.Unified()))
source, spans := result.Hunks(3)
fmt.Println(printer.Print(source, spans...))

Custom algorithms implement diff.Algorithm. For reusable differ instances:

differ := niceyaml.NewDiffer(niceyaml.WithAlgorithm(myAlgo))
result := differ.Diff(revA, revB)

The diff output uses line.Flag to mark inserted/deleted lines and line.Annotation for unified diff hunk headers.

Finder locates strings within tokens, returning position.Range values suitable for Source.AddOverlay.

Use StandardNormalizer with WithNormalizer for case-insensitive, diacritic-insensitive matching:

finder := niceyaml.NewFinder(niceyaml.WithNormalizer(niceyaml.NewStandardNormalizer()))
finder.Load(source)
for _, rng := range finder.Find("search term") {
	source.AddOverlay(style.GenericInserted, rng)
}

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoSource indicates no source was provided to resolve an error path.
	ErrNoSource = errors.New("no source provided")

	// ErrNoPathOrToken indicates neither a path nor token was provided.
	ErrNoPathOrToken = errors.New("no path or token provided")

	// ErrTokenNotFound indicates the token was not found in the source.
	ErrTokenNotFound = errors.New("token not found in source")
)
View Source
var PrettyEncoderOptions = []yaml.EncodeOption{
	yaml.Indent(2),
	yaml.IndentSequence(true),
}

PrettyEncoderOptions are default encoding options which can be used by NewEncoder to produce prettier-friendly YAML output.

Functions

This section is empty.

Types

type AnnotationContext

type AnnotationContext struct {
	Styles      StyleGetter
	Annotations line.Annotations
	Position    line.RelativePosition
}

AnnotationContext provides context for annotation rendering.

It is passed to AnnotationFunc to determine the appropriate annotation content.

type AnnotationFunc

type AnnotationFunc func(AnnotationContext) string

AnnotationFunc returns the rendered annotation content based on AnnotationContext.

func DefaultAnnotation

func DefaultAnnotation() AnnotationFunc

DefaultAnnotation creates an AnnotationFunc that renders annotations with position-based prefixes: "^ " for line.Below, none for line.Above.

type Decoder

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

Decoder iterates over YAML documents in a parsed *ast.File.

A single YAML file can contain multiple documents separated by "---". These documents often have different schemas and/or validation requirements. Decoder provides lazy iteration over these documents, providing a DocumentDecoder for each.

dec := niceyaml.NewDecoder(file)
for _, dd := range dec.Documents() {
	// Each dd is a DocumentDecoder instance.
}

Create instances with NewDecoder.

func NewDecoder

func NewDecoder(f *ast.File) *Decoder

NewDecoder creates a new *Decoder for the given *ast.File.

func (*Decoder) Documents

func (d *Decoder) Documents() iter.Seq2[int, *DocumentDecoder]

Documents returns an iterator over all documents in the YAML file.

Each iteration yields the document index and a *DocumentDecoder for that document.

func (*Decoder) Len

func (d *Decoder) Len() int

Len returns the number of YAML documents in the file.

type DiffResult

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

DiffResult holds computed diff operations for rendering.

Rendering methods:

Create instances with Differ.Diff or Diff.

func Diff

func Diff(a, b SourceGetter) *DiffResult

Diff computes the difference between two sources using the default algorithm.

This is a convenience function equivalent to NewDiffer().Diff(a, b).

func (*DiffResult) After

func (r *DiffResult) After() LineIterator

After returns a LineIterator for the right (after) pane of a side-by-side diff.

Lines are aligned with DiffResult.Before so both iterators have equal line counts. Consecutive delete/insert sequences are paired row-by-row. When there are more deletions than insertions, empty placeholder lines (zero value) fill the remaining rows on this side.

Line flags: line.FlagInserted for inserted lines, line.FlagDefault for equal lines and empty placeholders.

func (*DiffResult) Before

func (r *DiffResult) Before() LineIterator

Before returns a LineIterator for the left (before) pane of a side-by-side diff.

Lines are aligned with DiffResult.After so both iterators have equal line counts. Consecutive delete/insert sequences are paired row-by-row. When there are more insertions than deletions, empty placeholder lines (zero value) fill the remaining rows on this side.

Line flags: line.FlagDeleted for deleted lines, line.FlagDefault for equal lines and empty placeholders.

func (*DiffResult) Hunks

func (r *DiffResult) Hunks(context int) (*Source, position.Spans)

Hunks returns a *Source and line spans for rendering a summarized diff. The context parameter specifies the number of unchanged lines to show around each change. A context of 0 shows only the changed lines. Negative values are treated as 0.

The source contains all diff lines with flags for deleted/inserted lines. Hunk headers are stored in line.Annotation.Content for each hunk's first line.

Pass both to Printer.Print to render the hunks:

printer.Print(source, spans...)

func (*DiffResult) IsEmpty

func (r *DiffResult) IsEmpty() bool

IsEmpty reports whether the diff contains no lines.

func (*DiffResult) Name

func (r *DiffResult) Name() string

Name returns the diff name in "a..b" format.

func (*DiffResult) Stats

func (r *DiffResult) Stats() (int, int)

Stats returns the number of added and removed lines in the diff.

func (*DiffResult) Unified

func (r *DiffResult) Unified() *Source

Unified returns a *Source representing the complete diff.

The returned Source contains merged tokens from both revisions: unchanged lines use tokens from the second source, while changed lines include deleted tokens from the first source followed by inserted tokens from the second.

Source contains flags for deleted/inserted lines.

type Differ

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

Differ computes line differences using a configurable algorithm.

Differ is not safe for concurrent use because the underlying algorithm maintains reusable buffers. Create separate instances for concurrent operations. The returned *DiffResult is safe for concurrent use.

Create instances with NewDiffer.

func NewDiffer

func NewDiffer(opts ...DifferOption) *Differ

NewDiffer creates a new *Differ with the given options.

If no algorithm is specified, uses diff.Hirschberg.

func (*Differ) Diff

func (d *Differ) Diff(a, b SourceGetter) *DiffResult

Diff computes the difference between two sources.

The result can be rendered multiple times with DiffResult.Unified or DiffResult.Hunks.

type DifferOption

type DifferOption func(*Differ)

DifferOption configures a Differ.

Available options:

func WithAlgorithm

func WithAlgorithm(algo diff.Algorithm) DifferOption

WithAlgorithm sets the diff algorithm.

Default is diff.Hirschberg.

type DocumentDecoder

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

DocumentDecoder decodes and validates a single YAML document.

It separates decoding from document iteration, allowing validation hooks to run at the right time during unmarshaling. Types implementing SchemaValidator are validated before decoding, and types implementing Validator are validated after. Types may implement both interfaces.

Use DocumentDecoder.GetValue to inspect values without unmarshaling, which is helpful for routing documents based on a discriminator field.

For most use cases, call DocumentDecoder.Unmarshal to get the full validation pipeline:

for _, doc := range decoder.Documents() {
	var config Config
	if err := doc.Unmarshal(&config); err != nil {
		return err
	}
}

Use DocumentDecoder.Decode directly when you need decoding without validation hooks. All decoding methods convert YAML errors to Error with source annotations.

Create instances with NewDocumentDecoder.

func NewDocumentDecoder

func NewDocumentDecoder(doc *ast.DocumentNode) *DocumentDecoder

NewDocumentDecoder creates a new *DocumentDecoder for the given *ast.DocumentNode.

func (*DocumentDecoder) Decode

func (dd *DocumentDecoder) Decode(v any) error

Decode decodes the document into v.

This is a convenience wrapper around DocumentDecoder.DecodeContext with context.Background.

YAML decoding errors are converted to Error with source annotations.

func (*DocumentDecoder) DecodeContext

func (dd *DocumentDecoder) DecodeContext(ctx context.Context, v any) error

DecodeContext decodes the document into v with context.Context. YAML decoding errors are converted to Error with source annotations.

func (*DocumentDecoder) GetValue

func (dd *DocumentDecoder) GetValue(path *paths.YAMLPath) (string, bool)

GetValue extracts a raw YAML value without unmarshaling.

This is useful when you need to inspect document content before deciding how to process it. For example, multi-document files often use a discriminator field like "kind" or "version" to determine which schema applies:

kindPath := paths.Root().Child("kind").Path()
for _, doc := range decoder.Documents() {
	kind, _ := doc.GetValue(kindPath)
	switch kind {
	case "Pod":
		// Unmarshal to Pod struct.
	case "Service":
		// Unmarshal to Service struct.
	}
}

Returns the string representation of the value at path, or an empty string and false if path is nil, the document is a directive, or no value exists.

func (*DocumentDecoder) Unmarshal

func (dd *DocumentDecoder) Unmarshal(v any) error

Unmarshal validates and decodes the document into v.

This is a convenience wrapper around DocumentDecoder.UnmarshalContext with context.Background.

If v implements SchemaValidator, ValidateSchema is called before decoding. If v implements Validator, Validate is called after successful decoding.

func (*DocumentDecoder) UnmarshalContext

func (dd *DocumentDecoder) UnmarshalContext(ctx context.Context, v any) error

UnmarshalContext validates and decodes the document into v with context.Context.

If v implements SchemaValidator, ValidateSchema is called before decoding. If v implements Validator, Validate is called after successful decoding.

func (*DocumentDecoder) ValidateSchema

func (dd *DocumentDecoder) ValidateSchema(sv SchemaValidator) error

ValidateSchema decodes the document to [any] and validates it using sv.

This is a convenience wrapper around DocumentDecoder.ValidateSchemaContext with context.Background.

func (*DocumentDecoder) ValidateSchemaContext

func (dd *DocumentDecoder) ValidateSchemaContext(ctx context.Context, sv SchemaValidator) error

ValidateSchemaContext decodes the document to [any] and validates it using sv with context.Context.

Returns decoding errors or errors from the SchemaValidator ValidateSchema method.

type Encoder

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

Encoder wraps *yaml.Encoder for convenience.

Create instances with NewEncoder.

func NewEncoder

func NewEncoder(w io.Writer, opts ...yaml.EncodeOption) *Encoder

NewEncoder creates a new *Encoder that writes to w. Any provided [yaml.EncodeOption]s are passed to the underlying *yaml.Encoder.

func (*Encoder) Close

func (e *Encoder) Close() error

Close calls yaml.Encoder.Close.

func (*Encoder) Encode

func (e *Encoder) Encode(v any) error

Encode encodes v as YAML and writes it to the underlying writer.

type Error

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

Error represents a YAML error with optional source annotation.

To enable annotated error output that shows the relevant YAML location, provide:

Since these conditions must only be satisfied before calling Error.Error, you may use Error.SetOption to supply them at any time and in any context before then.

This means that callers may optionally attach any additional context that original Error producers might lack, thus avoiding the need for producers to take on any more responsibility than they need to.

For example, a SchemaValidator that produces Error values will be path-aware, and thus should use WithPath, but it will likely not have access to the Source.

For convenience, Source.WrapError can be used if you only need to add the Source without any other ErrorOption values.

Error implements the error interface. Use Error.Unwrap with errors.Is and errors.As to inspect wrapped errors.

Create instances with NewError or NewErrorFrom.

func NewError

func NewError(msg string, opts ...ErrorOption) *Error

NewError creates a new *Error with the given message. Use NewErrorFrom instead if wrapping an existing error.

func NewErrorFrom

func NewErrorFrom(err error, opts ...ErrorOption) *Error

NewErrorFrom creates a new *Error wrapping an existing error. Use NewError instead if creating an error from a message string.

func (Error) Error

func (e Error) Error() string

Error returns the error message with source annotation if available.

func (*Error) Path

func (e *Error) Path() string

Path returns the *paths.Path path where the error occurred as a string.

func (*Error) SetOption

func (e *Error) SetOption(opts ...ErrorOption)

SetOption applies the provided ErrorOption values to the Error.

func (*Error) SetWidth

func (e *Error) SetWidth(width int)

SetWidth sets the width for word wrapping of the error output. A width of 0 disables wrapping.

func (*Error) Unwrap

func (e *Error) Unwrap() []error

Unwrap returns the underlying errors, enabling errors.Is and errors.As.

type ErrorOption

type ErrorOption func(e *Error)

ErrorOption configures an Error.

Available options:

func WithErrorToken

func WithErrorToken(tk *token.Token) ErrorOption

WithErrorToken is an ErrorOption that sets the token where the error occurred.

func WithErrors

func WithErrors(errs ...*Error) ErrorOption

WithErrors is an ErrorOption that adds nested errors to the Error.

Each nested error has its own YAML path or token and is rendered as an annotation below its resolved line.

func WithPath

func WithPath(p *paths.Path) ErrorOption

WithPath is an ErrorOption that sets the YAML path where the error occurred.

The *paths.Path provides both the path and whether to highlight the key or value.

func WithPrinter

func WithPrinter(p WrappingPrinter) ErrorOption

WithPrinter is an ErrorOption that sets the WrappingPrinter used for formatting the error source.

func WithSource

func WithSource(src *Source) ErrorOption

WithSource is an ErrorOption that sets the *Source for resolving the error path.

func WithSourceLines

func WithSourceLines(lines int) ErrorOption

WithSourceLines is an ErrorOption that sets the number of context lines to show around the error.

func WithWidthFunc

func WithWidthFunc(fn func() int) ErrorOption

WithWidthFunc is an ErrorOption that sets a function to determine the width for word wrapping. This takes precedence over Error.SetWidth when both are configured.

type Finder

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

Finder finds strings within YAML tokens, returning position.Ranges that can be used to highlight matches in rendered output.

The typical use case is search-as-you-type highlighting: the user views YAML content and types a search term, and matching text is highlighted in place.

Finder solves the challenge of mapping string matches back to their original line and column positions, even when normalization (case folding, diacritic removal) changes the character count.

Finder uses a load-once, search-many design. Call Finder.Load once with your source data; this builds an internal index that maps character positions in the searchable text back to position.Position values in the original. Subsequent calls to Finder.Find use this index for efficient lookups without re-parsing.

Finder is safe for concurrent use. Multiple goroutines may call Finder.Find simultaneously, and Finder.Load uses locking to safely update internal state.

Example:

// Create finder with case-insensitive matching.
finder := niceyaml.NewFinder(
	niceyaml.WithNormalizer(niceyaml.NewStandardNormalizer()),
)
finder.Load(source)

// Find matches and highlight them.
for _, rng := range finder.Find("search term") {
	source.AddOverlay(highlightStyle, rng)
}
fmt.Println(printer.Print(source))

By default, searches are exact (case-sensitive, no normalization).

Use WithNormalizer with StandardNormalizer for case-insensitive matching that also ignores diacritics (e.g., "cafe" matches "Café").

Create instances with NewFinder.

func NewFinder

func NewFinder(opts ...FinderOption) *Finder

NewFinder creates a new *Finder. Call Finder.Load to provide a LineIterator before searching.

By default, no normalization is applied. Use WithNormalizer to enable case-insensitive or diacritic-insensitive matching.

func (*Finder) Find

func (f *Finder) Find(search string) []position.Range

Find finds all occurrences of the search string in the preprocessed source.

It returns a slice of position.Range indicating the start and end positions of each match. The slice is provided in the order the matches appear in the source.

Returns nil if the search string is empty or the finder has no source data.

func (*Finder) Load

func (f *Finder) Load(lines LineIterator)

Load preprocesses the given LineIterator, building the internal source string and position map for searching.

This method must be called before using Finder.Find.

type FinderOption

type FinderOption func(*Finder)

FinderOption configures a Finder.

Available options:

func WithNormalizer

func WithNormalizer(normalizer Normalizer) FinderOption

WithNormalizer is a FinderOption that sets a Normalizer applied to both the search string and source text before matching.

See StandardNormalizer for an implementation.

type GutterContext

type GutterContext struct {
	Styles     StyleGetter
	Index      int
	Number     int
	TotalLines int
	Flag       line.Flag
	Soft       bool
}

GutterContext provides context about the current line for gutter rendering. It is passed to GutterFunc to determine the appropriate gutter content.

type GutterFunc

type GutterFunc func(GutterContext) string

GutterFunc returns the gutter content for a line based on GutterContext. The returned string is rendered as the leftmost content before the line content.

Available gutters:

func DefaultGutter

func DefaultGutter() GutterFunc

DefaultGutter creates a GutterFunc that renders both line numbers and diff markers.

This is the default gutter used by NewPrinter.

func DiffGutter

func DiffGutter() GutterFunc

DiffGutter creates a GutterFunc that renders diff-style markers only (" ", "+", "-").

No line numbers are rendered.

Uses style.GenericInserted and style.GenericDeleted for styling.

func LineNumberGutter

func LineNumberGutter() GutterFunc

LineNumberGutter creates a GutterFunc that renders styled line numbers only.

For soft-wrapped continuation lines, renders " - " as a continuation marker.

No diff markers are rendered.

Uses style.Comment foreground for styling.

func NoGutter

func NoGutter() GutterFunc

NoGutter returns a GutterFunc that returns an empty string for all lines.

type LineGetter

type LineGetter interface {
	Lines() line.Lines
	Len() int
	IsEmpty() bool
}

LineGetter provides direct access to lines as a slice. See Source for an implementation.

type LineIterator

type LineIterator interface {
	AllLines(spans ...position.Span) iter.Seq2[position.Position, line.Line]
	AllRunes(ranges ...position.Range) iter.Seq2[position.Position, rune]
	Len() int
	IsEmpty() bool
}

LineIterator provides line-by-line access to YAML tokens. See Source for an implementation.

type Normalizer

type Normalizer interface {
	Normalize(in string) string
}

Normalizer transforms strings for comparison (e.g., removing diacritics).

See StandardNormalizer for an implementation.

type Printer

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

Printer prints YAML with syntax highlighting for terminal output.

It accepts a LineIterator, normally Source, and produces styled terminal output using [lipgloss.Style]s. It applies syntax highlighting to YAML tokens, with support for customizable gutters, annotations, styled overlays, and word wrapping.

Create instances with NewPrinter.

Rendering

Use Printer.Print to render lines. When called without spans, it renders all lines. Pass position.Span arguments to render specific line spans, which is useful for showing error context or diff hunks:

printer.Print(source)                   // All lines.
printer.Print(source, span1, span2)     // Specific spans.

Gutters

Gutters appear at the left edge of each line and typically show line numbers or diff markers. The printer uses DefaultGutter by default, which combines line numbers with diff markers (+/-). Other built-in options include DiffGutter (markers only), LineNumberGutter (numbers only), and NoGutter.

Overlays

Overlays apply visual highlighting to specific column spans within lines. Add overlays to a Source via Source.AddOverlay, then print normally. The printer blends overlay styles with the underlying token styles. This is how error positions and search results are highlighted.

Annotations

Annotations are extra text lines rendered above or below a line, outside the token stream. They display error messages, diff hunk headers, or other contextual notes. The printer renders them via AnnotationFunc, defaulting to DefaultAnnotation which prefixes below-line annotations with "^ ".

Word Wrapping

Call Printer.SetWidth to enable word wrapping at a given width. The printer accounts for gutter width when calculating available content width. Wrapped continuation lines show a "-" marker in the gutter.

func NewPrinter

func NewPrinter(opts ...PrinterOption) *Printer

NewPrinter creates a new *Printer. By default it uses theme.Charm, DefaultGutter, and DefaultAnnotation.

func (*Printer) Print

func (p *Printer) Print(lines LineIterator, spans ...position.Span) string

Print prints any LineIterator. It prints lines within the given [position.Span]s, in the supplied order. If no [position.Span]s are provided, all lines are printed.

func (*Printer) SetAnnotationsEnabled

func (p *Printer) SetAnnotationsEnabled(enabled bool)

SetAnnotationsEnabled sets whether annotations are rendered. Defaults to true.

func (*Printer) SetWidth

func (p *Printer) SetWidth(width int)

SetWidth sets the width for word wrapping. A width of 0 disables wrapping.

func (*Printer) SetWordWrap

func (p *Printer) SetWordWrap(enabled bool)

SetWordWrap sets whether word wrapping is enabled. Defaults to true. Word wrapping requires a width to be set via Printer.SetWidth.

func (*Printer) Style

func (p *Printer) Style(s style.Style) *lipgloss.Style

Style retrieves the underlying *lipgloss.Style for the given style.Style, or an empty style if not found.

type PrinterOption

type PrinterOption func(*Printer)

PrinterOption configures a Printer.

Available options:

func WithAnnotationFunc

func WithAnnotationFunc(fn AnnotationFunc) PrinterOption

WithAnnotationFunc is a PrinterOption that sets the AnnotationFunc for rendering annotations.

By default, DefaultAnnotation is used which adds "^ " prefix for line.Below annotations.

func WithGutter

func WithGutter(fn GutterFunc) PrinterOption

WithGutter is a PrinterOption that sets the GutterFunc for rendering. By default, DefaultGutter is used which renders line numbers and diff markers.

func WithStyle

func WithStyle(s lipgloss.Style) PrinterOption

WithStyle is a PrinterOption that configures the printer with the given container style.

func WithStyles

func WithStyles(s StyleGetter) PrinterOption

WithStyles is a PrinterOption that configures the printer with the given StyleGetter.

type Revision

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

Revision represents a *Source at one or more revisions. It may form a linked or doubly-linked list to track changes across revisions. A single revision is valid; multiple revisions are not required.

Create instances with NewRevision.

func NewRevision

func NewRevision(s *Source) *Revision

NewRevision creates a new *Revision with the given *Source at the head. Use Revision.Append or Revision.Prepend to add more revisions. A builder pattern is supported for values that are known at compile time.

func (*Revision) Append

func (t *Revision) Append(s *Source) *Revision

Append adds a new revision after the *Source at the head. Returns the newly added revision.

func (*Revision) At

func (t *Revision) At(index int) *Revision

At returns the revision at the given zero-based index. If index exceeds the available revisions, it stops at the last one. This is equivalent to Origin().Seek(index).

func (*Revision) AtOrigin

func (t *Revision) AtOrigin() bool

AtOrigin reports whether this is the original revision in the sequence.

func (*Revision) AtTip

func (t *Revision) AtTip() bool

AtTip reports whether this is the latest revision in the sequence.

func (*Revision) Index

func (t *Revision) Index() int

Index returns the zero-based index of the Revision at the head.

func (*Revision) Len

func (t *Revision) Len() int

Len returns the total number of revisions in the sequence.

func (*Revision) Name

func (t *Revision) Name() string

Name returns the name of the *Source at the head.

func (*Revision) Names

func (t *Revision) Names() []string

Names returns the names of all revisions in order from origin to latest.

func (*Revision) Origin

func (t *Revision) Origin() *Revision

Origin returns the original revision in the sequence.

func (*Revision) Prepend

func (t *Revision) Prepend(s *Source) *Revision

Prepend adds a new revision before the *Source at the head. Returns the newly added revision.

func (*Revision) Seek

func (t *Revision) Seek(n int) *Revision

Seek moves n revisions forward (n > 0) or backward (n < 0) in the sequence. If n exceeds the available revisions, it stops at the end.

func (*Revision) Source

func (t *Revision) Source() *Source

Source returns the *Source at the head.

func (*Revision) Tip

func (t *Revision) Tip() *Revision

Tip returns the latest revision in the sequence.

type SchemaValidator

type SchemaValidator interface {
	ValidateSchema(data any) error
}

SchemaValidator is implemented by types that validate arbitrary data against a schema.

If a type implements this interface, DocumentDecoder.Unmarshal automatically decodes the document to [any] and calls ValidateSchema before decoding to the typed struct.

See jacobcolvin.com/niceyaml/schema/validator.Validator for an implementation.

type Source

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

Source is the central type for parsing, displaying, and annotating YAML. It organizes YAML tokens into lines, enabling precise position tracking and styled rendering through Printer.

Typical use involves creating a Source, then passing it to utilities like Printer for rendering or Finder for searching:

source := NewSourceFromString(yamlContent)
printer := NewPrinter(WithStyles(theme.Charm()))
fmt.Println(printer.Print(source))

The token-based line structure enables features that would be difficult with string-based approaches: partial rendering of specific line ranges, accurate diff computation between YAML revisions, and search highlighting that respects token boundaries.

For structured access to the YAML content, use the Source.File method, which lazily parses the AST. Any parsing errors can be wrapped with source context using Source.WrapError for user-friendly error messages.

Overlay operations (for highlighting search results or diagnostics) are thread-safe; a Source can be highlighted from multiple goroutines while being rendered.

Create instances with NewSourceFromString, NewSourceFromToken, or NewSourceFromTokens.

func NewSourceFromString

func NewSourceFromString(src string, opts ...SourceOption) *Source

NewSourceFromString creates a new *Source from a YAML string using lexer.Tokenize.

func NewSourceFromToken

func NewSourceFromToken(tk *token.Token, opts ...SourceOption) *Source

NewSourceFromToken creates a new *Source from a seed *token.Token. It collects all token.Tokens by walking the token chain from start to end.

func NewSourceFromTokens

func NewSourceFromTokens(tks token.Tokens, opts ...SourceOption) *Source

NewSourceFromTokens creates a new *Source from token.Tokens. See line.NewLines for details on token splitting behavior.

func (*Source) AddOverlay

func (s *Source) AddOverlay(kind style.Style, ranges ...position.Range)

AddOverlay adds an overlay of the given kind to the specified ranges. Multi-line ranges are split into per-line overlays automatically.

func (*Source) AllLines

func (s *Source) AllLines(spans ...position.Span) iter.Seq2[position.Position, line.Line]

AllLines returns an iterator over lines within the given spans.

If no spans are provided, all lines are iterated. Each iteration yields a position.Position and the line.Line at that position.

func (*Source) AllRunes

func (s *Source) AllRunes(ranges ...position.Range) iter.Seq2[position.Position, rune]

AllRunes returns an iterator over runes within the given ranges. If no ranges are provided, all runes are iterated. Each iteration yields a position.Position and the rune at that position.

func (*Source) ClearOverlays

func (s *Source) ClearOverlays()

ClearOverlays removes all overlays from all lines.

func (*Source) Content

func (s *Source) Content() string

Content returns the combined content of all [line.Line]s as a string. Lines are joined with newlines.

func (*Source) ContentPositionRanges

func (s *Source) ContentPositionRanges(positions ...position.Position) []position.Range

ContentPositionRanges returns all position ranges for content at the given positions, excluding leading and trailing whitespace.

Duplicate ranges are removed.

Returns nil if no content exists at any of the given positions.

func (*Source) ContentPositionRangesFromToken

func (s *Source) ContentPositionRangesFromToken(tk *token.Token) []position.Range

ContentPositionRangesFromToken returns all position ranges for content of the given token, excluding leading and trailing whitespace.

Returns nil if the token is nil or not found in the Source.

func (*Source) File

func (s *Source) File() (*ast.File, error)

File returns an *ast.File for the Source tokens.

The file is lazily parsed on first call using parser.Parse with options provided via WithParserOptions. Subsequent calls return the cached result.

Any YAML parsing errors are converted to Error with source annotations.

func (*Source) IsEmpty

func (s *Source) IsEmpty() bool

IsEmpty reports whether there are no lines.

func (*Source) Len

func (s *Source) Len() int

Len returns the number of lines.

func (*Source) Line

func (s *Source) Line(idx int) *line.Line

Line returns the *line.Line at the given index. Panics if idx is out of range.

func (*Source) Lines

func (s *Source) Lines() line.Lines

Lines returns all line.Lines in the Source. This returns the internal slice for efficiency; callers should not modify it.

func (*Source) Name

func (s *Source) Name() string

Name returns the name of the Source.

func (*Source) String

func (s *Source) String() string

String reconstructs all [line.Line]s as a string, including any annotations. This should generally only be used for debugging.

func (*Source) TokenAt

func (s *Source) TokenAt(pos position.Position) *token.Token

TokenAt returns the *token.Token at the given position. Returns nil if the position is out of bounds or no token exists there.

func (*Source) TokenPositionRanges

func (s *Source) TokenPositionRanges(positions ...position.Position) []position.Range

TokenPositionRanges returns all token position ranges that are part of the same joined token group as the tokens at the given [position.Position]s.

For non-joined lines, returns the range of the token at each given column. Duplicate ranges are removed.

Returns nil if no tokens exist at any of the given positions.

func (*Source) TokenPositionRangesFromToken

func (s *Source) TokenPositionRangesFromToken(tk *token.Token) []position.Range

TokenPositionRangesFromToken returns all position ranges for a given token. Returns nil if the token is nil or not found in the Source.

func (*Source) Tokens

func (s *Source) Tokens() token.Tokens

Tokens reconstructs the full token.Tokens stream from all [line.Line]s. See line.Lines.Tokens for details on token recombination behavior.

func (*Source) Validate

func (s *Source) Validate() error

Validate checks the integrity of the Source. See line.Lines.Validate for details on validation checks.

func (*Source) Width

func (s *Source) Width() int

Width returns the maximum line width across all lines.

func (*Source) WrapError

func (s *Source) WrapError(err error) error

WrapError wraps an error with additional context for Error types. It applies any ErrorOption values and sets the source to this Source. If the error isn't an Error, it returns the original error unmodified.

type SourceGetter

type SourceGetter interface {
	Source() *Source
}

SourceGetter retrieves a *Source.

See Revision for an implementation.

type SourceOption

type SourceOption func(*Source)

SourceOption configures Source creation.

Available options:

func WithErrorOptions

func WithErrorOptions(opts ...ErrorOption) SourceOption

WithErrorOptions is a SourceOption that sets the ErrorOption values used when wrapping errors with Source.WrapError.

func WithName

func WithName(name string) SourceOption

WithName is a SourceOption that sets the name for the Source.

func WithParserOptions

func WithParserOptions(opts ...parser.Option) SourceOption

WithParserOptions is a SourceOption that sets the parser options used when parsing the Source into an *ast.File.

These options are passed to parser.Parse in addition to parser.ParseComments, which is always included.

type StandardNormalizer

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

StandardNormalizer removes diacritics and lowercases strings for case-insensitive matching. For example, "Ö" becomes "o". Note that unicode.Mn is the unicode key for nonspacing marks. Create instances with NewStandardNormalizer.

func NewStandardNormalizer

func NewStandardNormalizer() *StandardNormalizer

NewStandardNormalizer creates a new *StandardNormalizer.

func (*StandardNormalizer) Normalize

func (n *StandardNormalizer) Normalize(in string) string

Normalize implements Normalizer.

type StyleGetter

type StyleGetter interface {
	Style(s style.Style) *lipgloss.Style
}

StyleGetter retrieves styles by category.

See style.Styles for an implementation.

type Validator

type Validator interface {
	Validate() error
}

Validator is implemented by types that validate themselves.

If a type implements this interface, DocumentDecoder.Unmarshal automatically calls Validate after successful decoding.

type WrappingPrinter

type WrappingPrinter interface {
	SetWidth(width int)
	Print(lines LineIterator, spans ...position.Span) string
}

WrappingPrinter prints with width-aware word wrapping.

See Printer for an implementation.

Directories

Path Synopsis
bubbles
yamlviewport
Package yamlviewport provides a Bubble Tea component for viewing YAML with syntax highlighting, revision history, and diff visualization.
Package yamlviewport provides a Bubble Tea component for viewing YAML with syntax highlighting, revision history, and diff visualization.
cmd
nyaml command
Package main provides the nyaml CLI for viewing and validating YAML files.
Package main provides the nyaml CLI for viewing and validating YAML files.
Package diff computes minimal edit sequences between string slices.
Package diff computes minimal edit sequences between string slices.
examples
diffs command
finder command
printer command
schemas/cafe
Package cafe is an example.
Package cafe is an example.
schemas/cafe/schemagen command
Package main generates a JSON schema for the cafe example.
Package main generates a JSON schema for the cafe example.
schemas/cafe/spec
Package spec defines the cafe specification schema.
Package spec defines the cafe specification schema.
Package fangs provides CLI utilities for applications built with fang, a Cobra companion library.
Package fangs provides CLI utilities for applications built with fang, a Cobra companion library.
internal
ansi
Package ansi provides utilities for handling ANSI.
Package ansi provides utilities for handling ANSI.
colors
Package colors provides style combination utilities for layered styling.
Package colors provides style combination utilities for layered styling.
yamltest
Package yamltest provides test utilities for code that works with go-yaml tokens and niceyaml styled output.
Package yamltest provides test utilities for code that works with go-yaml tokens and niceyaml styled output.
Package lexers provides document-aware YAML tokenization.
Package lexers provides document-aware YAML tokenization.
Package line provides abstractions for line-by-line go-yaml Token processing.
Package line provides abstractions for line-by-line go-yaml Token processing.
Package paths builds YAML paths that distinguish between keys and values.
Package paths builds YAML paths that distinguish between keys and values.
Package position provides 0-indexed coordinates for locating and spanning regions within text documents.
Package position provides 0-indexed coordinates for locating and spanning regions within text documents.
Package schema provides common types for JSON Schema operations.
Package schema provides common types for JSON Schema operations.
generator
Package generator creates JSON schemas from Go types, bridging Go struct definitions to JSON Schema for YAML validation workflows.
Package generator creates JSON schemas from Go types, bridging Go struct definitions to JSON Schema for YAML validation workflows.
validator
Package validator validates YAML data against JSON Schema and returns errors with precise source locations for rich error display.
Package validator validates YAML data against JSON Schema and returns errors with precise source locations for rich error display.
Package style provides a hierarchical styling system for YAML syntax highlighting.
Package style provides a hierarchical styling system for YAML syntax highlighting.
theme
Package theme provides a catalog of pre-built color themes for YAML syntax highlighting.
Package theme provides a catalog of pre-built color themes for YAML syntax highlighting.
Package tokens provides segmentation for multiline YAML tokens and utilities for syntax highlighting.
Package tokens provides segmentation for multiline YAML tokens and utilities for syntax highlighting.

Jump to

Keyboard shortcuts

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