cellbuf

package
v0.0.0-...-64e189b Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package cellbuf is a retained grid of terminal cells: the fast path for a widget that wants to draw straight into cells instead of building a styled string that the renderer parses back every frame (spec S04). The View() string stays the primary way to draw; a Buffer is additive and converts to and from styled strings (Parse and Buffer.String), so the two compose.

A Buffer is a width x height grid. Each Cell holds one grapheme cluster, its column width (1, or 2 for a wide cluster) and a StyleID that indexes the Buffer's style table. A wide cluster occupies a head cell followed by a continuation cell (Width 0, empty Cluster). No operation ever leaves half of a wide cluster in the grid: writing over one half blanks the other, and a wide cluster that does not fit in the last column is replaced by a blank instead of being split.

Sub returns a clipped view that shares the same cells and styles, so a layout can hand each child its own rectangle to draw into.

Parsing follows the cell renderer's rules (the same SGR subset, OSC 8 hyperlinks, tabs expanded to spaces, grapheme clusters measured by an ansi.Measurer); a string the grid cannot represent (control characters other than tab, other escapes, unknown SGR codes) is rejected with an error wrapping ErrUnsupported.

Buffers are not safe for concurrent use. The package uses only the standard library and this module's ansi package.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrUnsupported = errors.New("cellbuf: unsupported string")

ErrUnsupported is wrapped by the errors Parse and SetStyled return for a string the cell grid cannot represent.

Functions

This section is empty.

Types

type Attr

type Attr uint16

Attr is a set of text attributes, as bit flags.

const (
	// AttrBold is SGR 1.
	AttrBold Attr = 1 << iota
	// AttrFaint is SGR 2.
	AttrFaint
	// AttrItalic is SGR 3.
	AttrItalic
	// AttrUnderline is SGR 4.
	AttrUnderline
	// AttrBlink is SGR 5.
	AttrBlink
	// AttrReverse is SGR 7.
	AttrReverse
	// AttrConceal is SGR 8.
	AttrConceal
	// AttrStrike is SGR 9.
	AttrStrike
)

The text attributes of a Style.

type Buffer

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

Buffer is a grid of cells, or a clipped view (see Sub) of one.

Example

Draw into a Buffer, then convert it to a styled string for a View.

package main

import (
	"fmt"

	"github.com/ows4444/tui/cellbuf"
)

func main() {
	b := cellbuf.New(6, 2)
	bold := b.StyleID(cellbuf.Style{Attrs: cellbuf.AttrBold})
	b.SetString(0, 0, "hi", bold)
	b.Sub(cellbuf.Rect{X: 3, Y: 1, W: 3, H: 1}).Fill(cellbuf.Rect{W: 3, H: 1}, "=", 0)
	fmt.Printf("%q\n", b.Lines())
}
Output:
["\x1b[1mhi\x1b[0m    " "   ==="]

func New

func New(width, height int) *Buffer

New returns a width x height Buffer of blank default-style cells. Negative sizes are taken as 0.

func Parse

func Parse(s string) (*Buffer, error)

Parse converts a styled string to a Buffer. Rows are separated by "\n" (a trailing "\r" on a row is dropped); the Buffer is as wide as the widest row and rows are padded with blanks. Each row starts from the default style, as in the cell renderer, so a row that leaves a style open does not affect the next. Parse("") is a Buffer of one empty row. A string the cell grid cannot represent is rejected with an error wrapping ErrUnsupported that names the renderer's reason.

Example

Parse and String convert between styled strings and cells.

package main

import (
	"fmt"

	"github.com/ows4444/tui/cellbuf"
)

func main() {
	b, err := cellbuf.Parse("\x1b[31mred\x1b[0m")
	fmt.Println(b.Width(), b.Style(b.At(0, 0).Style).FG == cellbuf.Basic(1), err)
	fmt.Printf("%q\n", b.String())
}
Output:
3 true <nil>
"\x1b[31mred\x1b[0m"

func ParseMeasured

func ParseMeasured(s string, m ansi.Measurer) (*Buffer, error)

ParseMeasured is Parse with widths measured by m, which the returned Buffer keeps (see SetMeasurer).

func (*Buffer) At

func (b *Buffer) At(x, y int) Cell

At returns the cell at column x, row y of b; a blank default cell when the position is outside b.

func (*Buffer) Bounds

func (b *Buffer) Bounds() Rect

Bounds returns b's own rectangle, {0, 0, Width, Height}.

func (*Buffer) Clear

func (b *Buffer) Clear()

Clear blanks every cell of b with the default style.

func (*Buffer) Fill

func (b *Buffer) Fill(r Rect, cluster string, id StyleID)

Fill sets every cell of r (relative to b, clipped to b) to repetitions of cluster in style id. An empty or unprintable cluster fills with spaces. A wide cluster is placed only whole: a column left over at the right edge of r is filled with a blank.

func (*Buffer) Height

func (b *Buffer) Height() int

Height returns the number of rows of b.

func (*Buffer) Lines

func (b *Buffer) Lines() []string

Lines returns one styled string per row of b. Every row starts from the default style and ends with it restored, so rows are independent.

func (*Buffer) SetMeasurer

func (b *Buffer) SetMeasurer(m ansi.Measurer)

SetMeasurer sets how cluster widths are measured by SetString, SetStyled, Fill and ParseMeasured, for every view of the same cells. The zero Measurer follows the process-wide setting of package ansi. Cells already in the Buffer are not re-measured.

func (*Buffer) SetRunes

func (b *Buffer) SetRunes(x, y int, r []rune, id StyleID) int

SetRunes is SetString for a rune slice: it writes r on row y from column x in style id and returns the columns written. Printable ASCII, the common case, is written without building a string.

func (*Buffer) SetString

func (b *Buffer) SetString(x, y int, s string, id StyleID) int

SetString writes the plain text s on row y starting at column x, in style id, and returns the number of columns it wrote. It does not wrap: text past the right edge, or above or left of b, is clipped. Escape sequences, newlines, tabs and other control characters in s are removed. A wide cluster never straddles the last column: where it would not fit, its visible column is blanked in id instead. Overwriting half of an existing wide cluster blanks the other half.

Example

A wide cluster is never split at the last column.

package main

import (
	"fmt"

	"github.com/ows4444/tui/cellbuf"
)

func main() {
	b := cellbuf.New(4, 1)
	b.SetString(0, 0, "a日日", 0)
	fmt.Printf("%q\n", b.String())
}
Output:
"a日 "

func (*Buffer) SetStyled

func (b *Buffer) SetStyled(x, y int, s string) (int, error)

SetStyled writes the styled string s (it may carry SGR sequences and OSC 8 hyperlinks, but no newline) on row y from column x, adding its styles to the style table, and returns the number of columns written. The pen starts as the default style. A string the cell grid cannot represent is rejected with an error wrapping ErrUnsupported and nothing is written. Clipping and wide clusters behave as in SetString.

func (*Buffer) String

func (b *Buffer) String() string

String returns the styled string form of b: its Lines joined by "\n". Parse(b.String()) gives back a Buffer with the same cells.

func (*Buffer) Style

func (b *Buffer) Style(id StyleID) Style

Style returns the style an id names; the zero Style for an unknown id.

func (*Buffer) StyleID

func (b *Buffer) StyleID(st Style) StyleID

StyleID adds st to the style table, if it is not there, and returns its id. Control characters are removed from st.Link. When the table is full (65535 styles) it returns 0, the default style.

func (*Buffer) Sub

func (b *Buffer) Sub(r Rect) *Buffer

Sub returns a view of the part of r (relative to b) that lies inside b. The view shares cells and styles with b, so drawing into it changes b, and its own coordinates start at r's corner. Drawing is clipped to the view. When a write lands on one half of a wide cluster that straddles the view's edge, the other half, outside the view, is blanked too so no half cluster remains.

func (*Buffer) Width

func (b *Buffer) Width() int

Width returns the number of columns of b.

type Cell

type Cell struct {
	// Cluster is the cell's grapheme cluster, "" for a continuation cell.
	Cluster string
	// Width is the columns the cluster covers: 1 or 2 for a head, 0 for a
	// continuation.
	Width uint8
	// Style is the cell's style in the owning Buffer's table.
	Style StyleID
}

Cell is one terminal column. A wide cluster occupies a head cell (Width 2) followed by a continuation cell (Width 0, empty Cluster). A blank cell is a single space of Width 1.

type Color

type Color uint32

Color is a terminal colour in the form a view used (basic, bright, 256 or RGB), so the terminal receives the same code family. The zero Color is the terminal's default colour. Colors are comparable.

func Basic

func Basic(n int) Color

Basic returns the basic colour n (0-7: black, red, green, yellow, blue, magenta, cyan, white); n is taken modulo 8.

func Bright

func Bright(n int) Color

Bright returns the bright colour n (0-7); n is taken modulo 8.

func Indexed

func Indexed(n int) Color

Indexed returns the 256-colour palette entry n; n is taken modulo 256.

func RGB

func RGB(r, g, b uint8) Color

RGB returns the 24-bit colour with the given components.

type Rect

type Rect struct {
	X, Y, W, H int
}

Rect is a rectangle of cells; X and Y are the top-left corner.

type Style

type Style struct {
	// Attrs are the text attributes.
	Attrs Attr
	// FG and BG are the foreground and background colours; zero is default.
	FG, BG Color
	// UnderlineStyle selects an extended underline shape (SGR 4:n): 2 double,
	// 3 curly, 4 dotted, 5 dashed. 0 is the plain underline, if AttrUnderline
	// is set.
	UnderlineStyle uint8
	// Link is the OSC 8 hyperlink target URI, "" for none. Control characters
	// are removed from it when the Style is added to a Buffer. A parsed
	// hyperlink's id and other parameters are not kept.
	Link string
}

Style is the look of a cell: attributes, colours and an optional hyperlink. The zero Style is the terminal default. Styles are comparable.

type StyleID

type StyleID uint16

StyleID names a Style in one Buffer's style table (and in every Sub view of it). ID 0 is always the zero Style. IDs are not portable between Buffers.

type Surface

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

Surface adapts a Buffer to layout.CellSurface, so a layout.CellNode tree can draw straight into the Buffer's cells (see layout.DrawTo). Coordinates are those of the Buffer it was made from. A Surface is not safe for concurrent use.

func Layout

func Layout(b *Buffer) *Surface

Layout returns a layout.CellSurface that draws into b, clipped to b.

func (*Surface) Clip

func (s *Surface) Clip() layout.Rect

Clip returns the rectangle drawing is currently clipped to.

func (*Surface) Put

func (s *Surface) Put(x, y int, str string) int

Put writes the styled string str (SGR sequences and OSC 8 links allowed, no newline) on row y from column x and returns the columns written. A string the grid cannot represent is written as plain text with its escapes removed.

func (*Surface) Repeat

func (s *Surface) Repeat(x, y, n int, cluster string)

Repeat writes n copies of cluster side by side on row y from column x.

func (*Surface) SetClip

func (s *Surface) SetClip(r layout.Rect)

SetClip clips later drawing to r, itself clipped to the Buffer.

Jump to

Keyboard shortcuts

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