Back to godoc.org

Package text

v0.0.0 (0022984)
Latest Go to latest
Published: May 13, 2020 | License: BSD-3-Clause | Module: golang.org/x/exp

Overview

Package text lays out paragraphs of text.

A body of text is laid out into a Frame: Frames contain Paragraphs (stacked vertically), Paragraphs contain Lines (stacked vertically), and Lines contain Boxes (stacked horizontally). Each Box holds a []byte slice of the text. For example, to simply print a Frame's text from start to finish:

var f *text.Frame = etc
for p := f.FirstParagraph(); p != nil; p = p.Next(f) {
	for l := p.FirstLine(f); l != nil; l = l.Next(f) {
		for b := l.FirstBox(f); b != nil; b = b.Next(f) {
			fmt.Print(b.Text(f))
		}
	}
}

A Frame's structure (the tree of Paragraphs, Lines and Boxes), and its []byte text, are not modified directly. Instead, a Frame's maximum width can be re-sized, and text can be added and removed via Carets (which implement standard io interfaces). For example, to add some words to the end of a frame:

var f *text.Frame = etc
c := f.NewCaret()
c.Seek(0, text.SeekEnd)
c.WriteString("Not with a bang but a whimper.\n")
c.Close()

Either way, such modifications can cause re-layout, which can add or remove Paragraphs, Lines and Boxes. The underlying memory for such structs can be re-used, so pointer values, such as of type *Box, should not be held over such modifications.

Example

Code:

// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package text_test

import (
	"fmt"
	"image"
	"os"

	"golang.org/x/exp/shiny/text"
	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

// toyFace implements the font.Face interface by measuring every rune's width
// as 1 pixel.
type toyFace struct{}

func (toyFace) Close() error {
	return nil
}

func (toyFace) Glyph(dot fixed.Point26_6, r rune) (image.Rectangle, image.Image, image.Point, fixed.Int26_6, bool) {
	panic("unimplemented")
}

func (toyFace) GlyphBounds(r rune) (fixed.Rectangle26_6, fixed.Int26_6, bool) {
	panic("unimplemented")
}

func (toyFace) GlyphAdvance(r rune) (fixed.Int26_6, bool) {
	return fixed.I(1), true
}

func (toyFace) Kern(r0, r1 rune) fixed.Int26_6 {
	return 0
}

func (toyFace) Metrics() font.Metrics {
	return font.Metrics{}
}

func printFrame(f *text.Frame, softReturnsOnly bool) {
	for p := f.FirstParagraph(); p != nil; p = p.Next(f) {
		for l := p.FirstLine(f); l != nil; l = l.Next(f) {
			for b := l.FirstBox(f); b != nil; b = b.Next(f) {
				if softReturnsOnly {
					os.Stdout.Write(b.TrimmedText(f))
				} else {
					os.Stdout.Write(b.Text(f))
				}
			}
			if softReturnsOnly {
				fmt.Println()
			}
		}
	}
}

func Example() {
	var f text.Frame
	f.SetFace(toyFace{})
	f.SetMaxWidth(fixed.I(60))

	c := f.NewCaret()
	c.WriteString(mobyDick)
	c.Close()

	fmt.Println("====")
	printFrame(&f, false)
	fmt.Println("====")
	fmt.Println("123456789_123456789_123456789_123456789_123456789_123456789_")
	printFrame(&f, true)
	fmt.Println("====")

	// Output:
	// ====
	// CHAPTER 1. Loomings.
	// Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...
	// ====
	// 123456789_123456789_123456789_123456789_123456789_123456789_
	// CHAPTER 1. Loomings.
	// Call me Ishmael. Some years ago—never mind how long
	// precisely—having little or no money in my purse, and nothing
	// particular to interest me on shore, I thought I would sail
	// about a little and see the watery part of the world...
	//
	// ====
}

const mobyDick = "CHAPTER 1. Loomings.\nCall me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...\n"
====
CHAPTER 1. Loomings.
Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...
====
123456789_123456789_123456789_123456789_123456789_123456789_
CHAPTER 1. Loomings.
Call me Ishmael. Some years ago—never mind how long
precisely—having little or no money in my purse, and nothing
particular to interest me on shore, I thought I would sail
about a little and see the watery part of the world...

====

Index

Examples

Package Files

Constants

const (
	SeekSet int = 0
	SeekCur int = 1
	SeekEnd int = 2
)

These constants are equal to os.SEEK_SET, os.SEEK_CUR and os.SEEK_END, understood by the io.Seeker interface, and are provided so that users of this package don't have to explicitly import "os".

type Box

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

Box holds a contiguous run of text.

func (*Box) Next

func (b *Box) Next(f *Frame) *Box

Next returns the next Box after this one in the Line.

f is the Frame that contains the Box.

func (*Box) Text

func (b *Box) Text(f *Frame) []byte

Text returns the Box's text.

f is the Frame that contains the Box.

func (*Box) TrimmedText

func (b *Box) TrimmedText(f *Frame) []byte

TrimmedText returns the Box's text, trimmed right of any white space if it is the last Box in its Line.

f is the Frame that contains the Box.

type Caret

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

Caret is a location in a Frame's text, and is the mechanism for adding and removing bytes of text. Conceptually, a Caret and a Frame's text is like an int c and a []byte t such that the text before and after that Caret is t[:c] and t[c:]. That byte-count location remains unchanged even when a Frame is re-sized and laid out into a new tree of Paragraphs, Lines and Boxes.

A Frame can have multiple open Carets. For example, the beginning and end of a text selection can be represented by two Carets. Multiple Carets for the one Frame are not safe to use concurrently, but it is valid to interleave such operations sequentially. For example, if two Carets c0 and c1 for the one Frame are positioned at the 10th and 20th byte, and 4 bytes are written to c0, inserting what becomes the equivalent of text[10:14], then c0's position is updated to be 14 but c1's position is also updated to be 24.

func (*Caret) Close

func (c *Caret) Close() error

Close closes the Caret.

func (*Caret) Delete

func (c *Caret) Delete(dir Direction, nBytes int) (dBytes int)

Delete deletes nBytes bytes in the specified direction from the Caret's location. It returns the number of bytes deleted, which can be fewer than that requested if it hits the beginning or end of the Frame.

func (*Caret) DeleteRunes

func (c *Caret) DeleteRunes(dir Direction, nRunes int) (dRunes, dBytes int)

DeleteRunes deletes nRunes runes in the specified direction from the Caret's location. It returns the number of runes and bytes deleted, which can be fewer than that requested if it hits the beginning or end of the Frame.

func (*Caret) Read

func (c *Caret) Read(buf []byte) (n int, err error)

Read satisfies the io.Reader interface by copying those bytes after the Caret and incrementing the Caret.

func (*Caret) ReadByte

func (c *Caret) ReadByte() (x byte, err error)

ReadByte returns the next byte after the Caret and increments the Caret.

func (*Caret) ReadRune

func (c *Caret) ReadRune() (r rune, size int, err error)

ReadRune returns the next rune after the Caret and increments the Caret.

func (*Caret) Seek

func (c *Caret) Seek(offset int64, whence int) (int64, error)

Seek satisfies the io.Seeker interface.

func (*Caret) Write

func (c *Caret) Write(s []byte) (n int, err error)

Write inserts s into the Frame's text at the Caret and increments the Caret.

func (*Caret) WriteByte

func (c *Caret) WriteByte(x byte) error

WriteByte inserts x into the Frame's text at the Caret and increments the Caret.

func (*Caret) WriteRune

func (c *Caret) WriteRune(r rune) (size int, err error)

WriteRune inserts r into the Frame's text at the Caret and increments the Caret.

func (*Caret) WriteString

func (c *Caret) WriteString(s string) (n int, err error)

WriteString inserts s into the Frame's text at the Caret and increments the Caret.

type Direction

type Direction bool

Direction is either forwards or backwards.

const (
	Forwards  Direction = false
	Backwards Direction = true
)

type Frame

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

Frame holds Paragraphs of text.

The zero value is a valid Frame of empty text, which contains one Paragraph, which contains one Line, which contains one Box.

func (*Frame) FirstParagraph

func (f *Frame) FirstParagraph() *Paragraph

FirstParagraph returns the first paragraph of this frame.

func (*Frame) Height

func (f *Frame) Height() int

Height returns the height in pixels of this Frame.

func (*Frame) Len

func (f *Frame) Len() int

Len returns the number of bytes in the Frame's text.

func (*Frame) LineCount

func (f *Frame) LineCount() int

LineCount returns the number of Lines in this Frame.

This count includes any soft returns inserted to wrap text to the maxWidth.

func (*Frame) NewCaret

func (f *Frame) NewCaret() *Caret

NewCaret returns a new Caret at the start of this Frame.

func (*Frame) ParagraphCount

func (f *Frame) ParagraphCount() int

ParagraphCount returns the number of Paragraphs in this Frame.

This count excludes any soft returns inserted to wrap text to the maxWidth.

func (*Frame) SetFace

func (f *Frame) SetFace(face font.Face)

SetFace sets the font face for measuring text.

func (*Frame) SetMaxWidth

func (f *Frame) SetMaxWidth(m fixed.Int26_6)

SetMaxWidth sets the target maximum width of a Line of text, as a fixed-point fractional number of pixels. Text will be broken so that a Line's width is less than or equal to this maximum width. This line breaking is not strict. A Line containing asingleverylongword combined with a narrow maximum width will not be broken and will remain longer than the target maximum width; soft hyphens are not inserted.

A non-positive argument is treated as an infinite maximum width.

type Line

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

Line holds Boxes of text.

func (*Line) FirstBox

func (l *Line) FirstBox(f *Frame) *Box

FirstBox returns the first Box of this Line.

f is the Frame that contains the Line.

func (*Line) Height

func (l *Line) Height(f *Frame) int

Height returns the height in pixels of this Line.

func (*Line) Next

func (l *Line) Next(f *Frame) *Line

Next returns the next Line after this one in the Paragraph.

f is the Frame that contains the Line.

type Paragraph

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

Paragraph holds Lines of text.

func (*Paragraph) FirstLine

func (p *Paragraph) FirstLine(f *Frame) *Line

FirstLine returns the first Line of this Paragraph.

f is the Frame that contains the Paragraph.

func (*Paragraph) Height

func (p *Paragraph) Height(f *Frame) int

Height returns the height in pixels of this Paragraph.

func (*Paragraph) LineCount

func (p *Paragraph) LineCount(f *Frame) int

LineCount returns the number of Lines in this Paragraph.

This count includes any soft returns inserted to wrap text to the maxWidth.

func (*Paragraph) Next

func (p *Paragraph) Next(f *Frame) *Paragraph

Next returns the next Paragraph after this one in the Frame.

f is the Frame that contains the Paragraph.

Documentation was rendered with GOOS=linux and GOARCH=amd64.

Jump to identifier

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to identifier