Documentation
¶
Overview ¶
Package prettyview provides a memory-efficient, virtualized Fyne widget for viewing structured data — JSON, JSONC, XML, HTML, and raw text — in the style of Bruno's response viewer: syntax highlighting, per-node expand/fold with collapse summaries, copy-subtree, true character-level free-text selection, and incremental search.
The widget is built around a hard memory bound: only the rows currently visible in the viewport ever exist as live fyne.CanvasObjects. Everything else lives in a compact struct-of-arrays document model, and selection, search, and copy all operate on that model rather than on widgets. As a result a multi-megabyte document occupies a small, predictable number of canvas objects regardless of its size.
Threading ¶
PrettyView follows the standard Fyne widget threading model: it is NOT safe for concurrent use. Every method — including the data, fold, selection, and search mutators (SetData, SetText, Reparse, ExpandAll, CollapseAll, ExpandTo, Search, SearchNext/Prev, ClearSearch, SelectAll, ClearSelection, SetTheme, …) — must be called on the goroutine that runs the Fyne event loop (the "main"/Fyne goroutine), exactly like any other Fyne widget. To drive the widget from another goroutine (e.g. after a network fetch), marshal the call with fyne.Do:
go func() {
data := fetch()
fyne.Do(func() { pv.SetData(data, prettyview.FormatAuto) })
}()
The widget intentionally holds no locks: all of its state (the document, the fold index, and the selection/search state) is owned by the Fyne goroutine, so single-threaded access is a precondition rather than something guarded at runtime. The only work that runs off that goroutine is the search-debounce timer (time.AfterFunc); it marshals its scan back via fyne.Do and drops superseded scans via a generation counter, so it never touches widget state concurrently.
Index ¶
- Constants
- func NewFoldButtons(pv *PrettyView) fyne.CanvasObject
- func NewFormatSelect(pv *PrettyView) fyne.CanvasObject
- func NewSearchBar(pv *PrettyView) fyne.CanvasObject
- func NewToolbar(pv *PrettyView, cfg ToolbarConfig) fyne.CanvasObject
- func NewWrapToggle(pv *PrettyView) fyne.CanvasObject
- func ShowOpenDialog(pv *PrettyView, win fyne.Window)
- type Format
- type Match
- type Option
- func WithDefaultCollapseDepth(d int) Option
- func WithFormat(f Format) Option
- func WithIndentStep(px float32) Option
- func WithMaxInputBytes(n int) Option
- func WithSearchConfig(s SearchConfig) Option
- func WithSyntaxColors(v fyne.ThemeVariant, s SyntaxColors) Option
- func WithTabWidth(n int) Option
- func WithTheme(v fyne.ThemeVariant, t Theme) Option
- func WithWrap(m WrapMode) Option
- type PrettyView
- func (pv *PrettyView) ClearSearch()
- func (pv *PrettyView) ClearSelection()
- func (pv *PrettyView) CollapseAll()
- func (pv *PrettyView) CopySelection()
- func (pv *PrettyView) CopySubtree(byteOffset int) bool
- func (pv *PrettyView) CreateRenderer() fyne.WidgetRenderer
- func (pv *PrettyView) Cursor() desktop.Cursor
- func (pv *PrettyView) DragEnd()
- func (pv *PrettyView) Dragged(ev *fyne.DragEvent)
- func (pv *PrettyView) ExpandAll()
- func (pv *PrettyView) ExpandTo(off int) bool
- func (pv *PrettyView) FocusGained()
- func (pv *PrettyView) FocusLost()
- func (pv *PrettyView) Format() Format
- func (pv *PrettyView) MouseDown(ev *desktop.MouseEvent)
- func (pv *PrettyView) MouseIn(*desktop.MouseEvent)
- func (pv *PrettyView) MouseMoved(ev *desktop.MouseEvent)
- func (pv *PrettyView) MouseOut()
- func (pv *PrettyView) MouseUp(*desktop.MouseEvent)
- func (pv *PrettyView) Reparse(format Format)
- func (pv *PrettyView) Search(q SearchQuery)
- func (pv *PrettyView) SearchDebounced(q SearchQuery)
- func (pv *PrettyView) SearchError() error
- func (pv *PrettyView) SearchNext()
- func (pv *PrettyView) SearchPrev()
- func (pv *PrettyView) SearchStatus() (active, total int, capped bool)
- func (pv *PrettyView) SelectAll()
- func (pv *PrettyView) SelectedText() string
- func (pv *PrettyView) SetData(src []byte, format Format)
- func (pv *PrettyView) SetDefaultCollapseDepth(depth int)
- func (pv *PrettyView) SetOnDataChanged(fn func())
- func (pv *PrettyView) SetOnSearchChanged(fn func())
- func (pv *PrettyView) SetOnSearchRequested(fn func())
- func (pv *PrettyView) SetSyntaxColors(variant fyne.ThemeVariant, c SyntaxColors)
- func (pv *PrettyView) SetText(s string)
- func (pv *PrettyView) SetTheme(variant fyne.ThemeVariant, t Theme)
- func (pv *PrettyView) SetWrap(mode WrapMode)
- func (pv *PrettyView) Source() []byte
- func (pv *PrettyView) Tapped(e *fyne.PointEvent)
- func (pv *PrettyView) TappedSecondary(e *fyne.PointEvent)
- func (pv *PrettyView) TypedKey(ev *fyne.KeyEvent)
- func (pv *PrettyView) TypedRune(rune)
- func (pv *PrettyView) TypedShortcut(s fyne.Shortcut)
- func (pv *PrettyView) Wrap() WrapMode
- type SearchConfig
- type SearchMode
- type SearchQuery
- type SyntaxColors
- type Theme
- type ToolbarConfig
- type WrapMode
Constants ¶
const ( FormatAuto = model.FormatAuto // run AutoDetect heuristics FormatRaw = model.FormatRaw // plain text, split into physical lines FormatJSON = model.FormatJSON // strict JSON FormatJSONC = model.FormatJSONC // JSON with // and /* */ comments FormatXML = model.FormatXML // XML FormatHTML = model.FormatHTML // HTML (tolerant) )
Variables ¶
This section is empty.
Functions ¶
func NewFoldButtons ¶
func NewFoldButtons(pv *PrettyView) fyne.CanvasObject
NewFoldButtons returns an expand-all / collapse-all icon pair bound to pv.
func NewFormatSelect ¶
func NewFormatSelect(pv *PrettyView) fyne.CanvasObject
NewFormatSelect returns a format selector bound to pv. Choosing a format re-parses the current source; the selection follows the document when content is loaded elsewhere (it registers PrettyView's data-changed hook).
func NewSearchBar ¶
func NewSearchBar(pv *PrettyView) fyne.CanvasObject
NewSearchBar returns a find box (with case-sensitive and regex toggles, prev/next, and a self-updating match counter) bound to pv. It registers PrettyView's search-changed and search-requested hooks so the counter stays in sync and Ctrl/Cmd+F focuses it. Enter finds next, Shift+Enter finds previous, Esc clears.
func NewToolbar ¶
func NewToolbar(pv *PrettyView, cfg ToolbarConfig) fyne.CanvasObject
NewToolbar builds an optional control bar bound to pv from cfg. Disabled controls are omitted. The result is a plain fyne.CanvasObject; place it where you like (typically the top of a container.NewBorder around the PrettyView).
func NewWrapToggle ¶
func NewWrapToggle(pv *PrettyView) fyne.CanvasObject
NewWrapToggle returns a wrap-text icon toggle bound to pv: it flips between soft-wrap (WrapWord) and horizontal scroll (WrapNone), and is highlighted (HighImportance) while wrapping is on so the state is visible.
func ShowOpenDialog ¶
func ShowOpenDialog(pv *PrettyView, win fyne.Window)
ShowOpenDialog opens Fyne's built-in file-open dialog (an in-canvas widget, not the OS-native picker — Fyne draws its own UI) and loads the chosen file into pv, auto-detecting the format. Exposed so hosts can trigger it from their own menu/button. A host that wants the platform-native dialog should instead set ToolbarConfig.OnOpen (or call its own picker) and feed the bytes to pv.SetData.
Types ¶
type Format ¶
Format selects (or, with FormatAuto, detects) the input grammar. It is an alias of the model package's type so the document model and the public API share one type while keeping the model internal.
type Match ¶
Match is one search hit, in model coordinates: a stable display-line index and a rune column range into that line's expanded text. Keying by line (not visible row) makes matches survive folding; the visible row is an O(log n) lookup.
type Option ¶
type Option func(*config)
Option customizes a PrettyView at construction time.
func WithDefaultCollapseDepth ¶
WithDefaultCollapseDepth auto-collapses every container at nesting depth d or deeper on load. Top-level containers are at depth 0, so d=1 collapses everything below the root and d=0 (or any d <= 0) disables auto-collapse.
func WithFormat ¶
WithFormat forces a specific input format, skipping auto-detection.
func WithIndentStep ¶
WithIndentStep sets the pixels of indentation per nesting level.
func WithMaxInputBytes ¶
WithMaxInputBytes caps the source size SetData/SetText will load. Input longer than n bytes is truncated to n before parsing (the parsers are tolerant, so a truncated document still renders). It bounds the synchronous parse work and the resulting model size (≈5–7× the source, built on the calling/Fyne goroutine) for untrusted or unbounded input. n <= 0 (the default) imposes no cap beyond the model's 4 GiB source ceiling.
func WithSearchConfig ¶
func WithSearchConfig(s SearchConfig) Option
WithSearchConfig overrides the search tuning parameters. The struct is used as given — it replaces the defaults wholesale, not field-by-field. A zero MaxMatches or MinQueryLen still falls back to its default at scan time, but a zero DebounceFor means "no debounce" (scan on every keystroke), NOT 150 ms; set it explicitly to keep keystroke coalescing.
func WithSyntaxColors ¶
func WithSyntaxColors(v fyne.ThemeVariant, s SyntaxColors) Option
WithSyntaxColors overrides just the syntax token colors for a theme variant (shorthand for WithTheme with only the token fields set).
func WithTabWidth ¶
WithTabWidth sets the display width of a tab character (default 4).
type PrettyView ¶
type PrettyView struct {
widget.BaseWidget
// contains filtered or unexported fields
}
PrettyView is the virtualized structured-data viewer widget.
All state is unexported. Construct one with New or NewWithData and feed it content with SetData / SetText.
PrettyView is not safe for concurrent use: call its methods on the Fyne goroutine (see the package doc's Threading section).
func New ¶
func New(opts ...Option) *PrettyView
New constructs an empty PrettyView, applying zero or more Options.
func NewWithData ¶
func NewWithData(src []byte, format Format, opts ...Option) *PrettyView
NewWithData constructs a PrettyView and immediately parses src under format.
func (*PrettyView) ClearSearch ¶
func (pv *PrettyView) ClearSearch()
ClearSearch clears search state and highlights. It also cancels any pending debounced scan so a stale query can't repopulate matches after a clear (this is the path SetData uses, so loading new data drops the old document's pending search too).
func (*PrettyView) ClearSelection ¶
func (pv *PrettyView) ClearSelection()
ClearSelection drops any selection.
func (*PrettyView) CollapseAll ¶
func (pv *PrettyView) CollapseAll()
CollapseAll collapses every node below the top level.
func (*PrettyView) CopySelection ¶
func (pv *PrettyView) CopySelection()
CopySelection copies SelectedText to the clipboard (no-op if empty).
func (*PrettyView) CopySubtree ¶
func (pv *PrettyView) CopySubtree(byteOffset int) bool
CopySubtree copies the displayed text of the node owning byteOffset (its whole {…}/[…] span, regardless of fold state) to the clipboard, reporting whether a node was found and copied. Source byte offsets are populated for JSON/JSONC only; XML and HTML nodes carry no source span, so CopySubtree returns false for them (copying an XML/HTML subtree by offset is not yet supported). The copied text is the viewer's pretty-printed rendering of the subtree, not the original bytes.
func (*PrettyView) CreateRenderer ¶
func (pv *PrettyView) CreateRenderer() fyne.WidgetRenderer
CreateRenderer implements fyne.Widget. It builds the scroll + layered content and wires scrolling to the visible-window reflow.
func (*PrettyView) Cursor ¶
func (pv *PrettyView) Cursor() desktop.Cursor
Cursor reports the pointer shape: a pointer over a fold triangle, the text I-beam elsewhere. The over-triangle flag is updated by hover tracking.
func (*PrettyView) DragEnd ¶
func (pv *PrettyView) DragEnd()
func (*PrettyView) Dragged ¶
func (pv *PrettyView) Dragged(ev *fyne.DragEvent)
func (*PrettyView) ExpandTo ¶
func (pv *PrettyView) ExpandTo(off int) bool
ExpandTo expands every collapsed ancestor of the node owning byte offset off and scrolls it into view, reporting whether such a node was found. Source byte offsets are populated for JSON/JSONC only; XML and HTML carry no per-node source span (their tokenizers don't expose one), so on those formats no node matches an arbitrary offset and ExpandTo returns false without scrolling.
func (*PrettyView) FocusGained ¶
func (pv *PrettyView) FocusGained()
func (*PrettyView) FocusLost ¶
func (pv *PrettyView) FocusLost()
func (*PrettyView) Format ¶
func (pv *PrettyView) Format() Format
Format reports the format actually used for the current document.
func (*PrettyView) MouseDown ¶
func (pv *PrettyView) MouseDown(ev *desktop.MouseEvent)
func (*PrettyView) MouseIn ¶
func (pv *PrettyView) MouseIn(*desktop.MouseEvent)
func (*PrettyView) MouseMoved ¶
func (pv *PrettyView) MouseMoved(ev *desktop.MouseEvent)
func (*PrettyView) MouseOut ¶
func (pv *PrettyView) MouseOut()
func (*PrettyView) MouseUp ¶
func (pv *PrettyView) MouseUp(*desktop.MouseEvent)
func (*PrettyView) Reparse ¶
func (pv *PrettyView) Reparse(format Format)
Reparse re-parses the current source under a different format (e.g. when a UI lets the user override auto-detection). No-op if no document is loaded.
func (*PrettyView) Search ¶
func (pv *PrettyView) Search(q SearchQuery)
Search starts or replaces the active search and reveals the first match. It runs synchronously and returns once matches are computed.
func (*PrettyView) SearchDebounced ¶
func (pv *PrettyView) SearchDebounced(q SearchQuery)
SearchDebounced runs Search(q) after the configured SearchConfig.DebounceFor delay, coalescing a burst of rapid calls (e.g. one per keystroke from a host's own search field) so only the last query in the burst scans. With DebounceFor <= 0 it is equivalent to Search (immediate). Last call wins: a newer SearchDebounced / Search / ClearSearch / SetData supersedes a still-pending scan. Call it on the Fyne goroutine.
func (*PrettyView) SearchError ¶
func (pv *PrettyView) SearchError() error
SearchError reports the error from the most recent Search / SearchDebounced, or nil. The only error is an invalid regular expression (a SearchRegex query whose pattern does not compile); it lets a caller distinguish "the pattern is bad" from "the pattern is valid but matched nothing". It is cleared by the next Search/ClearSearch.
func (*PrettyView) SearchNext ¶
func (pv *PrettyView) SearchNext()
SearchNext moves to the next match (wrapping) and reveals it.
func (*PrettyView) SearchPrev ¶
func (pv *PrettyView) SearchPrev()
SearchPrev moves to the previous match (wrapping) and reveals it.
func (*PrettyView) SearchStatus ¶
func (pv *PrettyView) SearchStatus() (active, total int, capped bool)
SearchStatus returns the active 1-based index, the total match count, and whether the count was capped.
func (*PrettyView) SelectAll ¶
func (pv *PrettyView) SelectAll()
SelectAll selects the entire (currently visible) document.
func (*PrettyView) SelectedText ¶
func (pv *PrettyView) SelectedText() string
SelectedText returns the exact text currently selected, or "".
func (*PrettyView) SetData ¶
func (pv *PrettyView) SetData(src []byte, format Format)
SetData parses src under format (FormatAuto detects) and refreshes the view.
Parsing is SYNCHRONOUS on the calling goroutine (the Fyne goroutine), and builds a compact model ≈5–7× the source size. That is fast for multi-megabyte input but is still O(source) work done before this call returns; to load a large document without a UI hitch, parse off-thread is not supported, so either keep inputs bounded (see WithMaxInputBytes) or accept a brief synchronous cost. Input longer than WithMaxInputBytes (if set) is truncated before parsing.
The src slice is retained: the model holds zero-copy byte ranges into it, so callers must not mutate src after this call (copy it first if it may change).
func (*PrettyView) SetDefaultCollapseDepth ¶
func (pv *PrettyView) SetDefaultCollapseDepth(depth int)
SetDefaultCollapseDepth sets the auto-collapse depth applied on subsequent SetData calls (0 disables).
func (*PrettyView) SetOnDataChanged ¶
func (pv *PrettyView) SetOnDataChanged(fn func())
SetOnDataChanged registers a callback invoked whenever the document is replaced (SetData/SetText/Reparse). Use it to keep host controls (such as a format selector) in sync. Setting it replaces any previous callback.
func (*PrettyView) SetOnSearchChanged ¶
func (pv *PrettyView) SetOnSearchChanged(fn func())
SetOnSearchChanged registers a callback invoked whenever the search match set or active match changes. Use it to keep a host match counter in sync. Setting it replaces any previous callback.
func (*PrettyView) SetOnSearchRequested ¶
func (pv *PrettyView) SetOnSearchRequested(fn func())
SetOnSearchRequested registers a callback invoked when the user presses the search shortcut (Ctrl/Cmd+F), so a host can focus its search field.
func (*PrettyView) SetSyntaxColors ¶
func (pv *PrettyView) SetSyntaxColors(variant fyne.ThemeVariant, c SyntaxColors)
SetSyntaxColors overrides just the syntax token colors for a theme variant and refreshes (shorthand for SetTheme with only the token fields set).
func (*PrettyView) SetText ¶
func (pv *PrettyView) SetText(s string)
SetText is shorthand for SetData([]byte(s), FormatAuto).
func (*PrettyView) SetTheme ¶
func (pv *PrettyView) SetTheme(variant fyne.ThemeVariant, t Theme)
SetTheme overrides any of the viewer's colors for a theme variant and refreshes. Nil fields keep their defaults; calls compose with earlier WithTheme/WithSyntaxColors/SetTheme overrides for that variant.
func (*PrettyView) SetWrap ¶
func (pv *PrettyView) SetWrap(mode WrapMode)
SetWrap switches long-line handling between WrapNone (horizontal scroll) and WrapWord (soft-wrap to the viewport width) and refreshes. Wrapping is purely presentational: the model, selection, search, and copy are unchanged — a wrapped line still copies as one logical line.
func (*PrettyView) Source ¶
func (pv *PrettyView) Source() []byte
Source returns the bytes of the current document (the originally supplied input), or nil.
func (*PrettyView) Tapped ¶
func (pv *PrettyView) Tapped(e *fyne.PointEvent)
Tapped toggles a fold when the tap lands on a fold triangle. Other taps are left to the selection layer (M8).
func (*PrettyView) TappedSecondary ¶
func (pv *PrettyView) TappedSecondary(e *fyne.PointEvent)
TappedSecondary shows the context menu at the click. This is the standard Fyne pop-up menu — the same themed overlay Entry and read-only selectable text use for their right-click menus (Fyne draws its own UI on the GL canvas, so there is no native OS menu to invoke). A right-click never disturbs the selection (MouseDown returns early for the secondary button), so "Copy" acts on whatever the user had already highlighted.
func (*PrettyView) TypedKey ¶
func (pv *PrettyView) TypedKey(ev *fyne.KeyEvent)
TypedKey handles Escape (clear selection) and keyboard scrolling/navigation: Up/Down scroll one row, PageUp/PageDown one viewport, Home/End jump to the top/ bottom. A multi-megabyte viewer should be navigable without the mouse.
func (*PrettyView) TypedRune ¶
func (pv *PrettyView) TypedRune(rune)
func (*PrettyView) TypedShortcut ¶
func (pv *PrettyView) TypedShortcut(s fyne.Shortcut)
func (*PrettyView) Wrap ¶
func (pv *PrettyView) Wrap() WrapMode
Wrap reports the current long-line handling mode.
type SearchConfig ¶
type SearchConfig struct {
MaxMatches int // cap on stored matches (default 10_000)
DebounceFor time.Duration // keystroke debounce (default 150ms)
MinQueryLen int // shortest query that triggers a scan (default 1)
}
SearchConfig tunes the incremental search behavior. The scan is synchronous on the Fyne goroutine (debounced, and bounded by MaxMatches); see runSearch.
type SearchMode ¶
type SearchMode uint8
SearchMode selects plain-substring or regular-expression matching.
const ( // SearchPlain matches the query as a literal substring (the default). SearchPlain SearchMode = iota // SearchRegex matches the query as a Go regular expression (regexp/RE2). RE2 // matches in time linear in the input, so a host-supplied pattern cannot cause // catastrophic backtracking (no ReDoS); an invalid pattern is reported via // SearchError. A pathological pattern can still be expensive to compile, so treat // the pattern as you would any host input. SearchRegex )
type SearchQuery ¶
type SearchQuery struct {
Text string
Mode SearchMode
CaseSensitive bool
}
SearchQuery describes an incremental search request.
type SyntaxColors ¶
type SyntaxColors struct {
Key color.Color
String color.Color
Number color.Color
Bool color.Color
Null color.Color
Punct color.Color
Tag color.Color
Attr color.Color
Comment color.Color
}
SyntaxColors is the token-color subset of Theme, kept for the common case of recoloring only the syntax. A nil field keeps the default.
type Theme ¶
type Theme struct {
// Syntax token colors.
Key color.Color
String color.Color
Number color.Color
Bool color.Color
Null color.Color
Punct color.Color
Tag color.Color
Attr color.Color
Comment color.Color
// Structural / UI colors.
Foreground color.Color // default ("plain") text and punctuation fallback
Summary color.Color // fold-summary text, e.g. "{ 6 items }"
IndentGuide color.Color // the vertical indent guide lines
Selection color.Color // free-text selection highlight fill
Match color.Color // inactive search-match highlight fill
ActiveMatch color.Color // active search-match highlight fill
}
Theme is the full set of colors the viewer draws. Any nil field falls back to the built-in default for the active theme variant — and the structural defaults (Foreground, Summary, IndentGuide, Selection) themselves follow the host Fyne app theme, so an un-themed PrettyView blends into its surroundings.
Override per variant with WithTheme (construction) or SetTheme (at runtime). Overrides compose: setting only a few fields leaves the rest at their default, and repeated calls / WithSyntaxColors merge rather than replace.
type ToolbarConfig ¶
type ToolbarConfig struct {
ShowOpen bool // an "Open…" button (needs Window or OnOpen)
ShowFormat bool // a format selector (auto/json/jsonc/xml/html/raw)
ShowExpandCollapse bool // Expand all / Collapse all buttons
ShowWrap bool // a wrap-text icon toggle (soft-wrap on/off)
ShowSearch bool // a find box with prev/next and a match counter
Window fyne.Window // enables the built-in Open dialog and Ctrl/Cmd+F focus
OnOpen func() // overrides the built-in Open behavior, if set
}
ToolbarConfig selects which built-in controls NewToolbar includes. Each is optional; leave a field false to omit that control and provide your own.
func DefaultToolbarConfig ¶
func DefaultToolbarConfig() ToolbarConfig
DefaultToolbarConfig enables every control. Note that two of them still need more than the flag: the Open button requires a Window (for the built-in dialog) or an OnOpen handler, and Ctrl/Cmd+F search-focus requires a Window — set Window on the returned config (e.g. cfg := DefaultToolbarConfig(); cfg.Window = w) or those two are silently omitted. (Taking the Window as a parameter is a deferred v1.0 change.)
type WrapMode ¶
type WrapMode uint8
WrapMode controls long-line handling. WrapNone lets long lines overflow and be reached by horizontal scrolling (matching Bruno); WrapWord soft-wraps them to the viewport width (breaking at word boundaries, with a char-break fallback for an unbreakable run). Wrapping is presentational only — selection, search, and copy still operate on whole logical lines.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
prettyview-demo
command
Command prettyview-demo exercises the prettyview widget.
|
Command prettyview-demo exercises the prettyview widget. |
|
Package fonttheme bundles the typefaces go-fyne-pretty-view ships with — JetBrains Mono for monospace (the viewer body) and Inter for UI text — and exposes them as a fyne.Theme you can install on your app.
|
Package fonttheme bundles the typefaces go-fyne-pretty-view ships with — JetBrains Mono for monospace (the viewer body) and Inter for UI text — and exposes them as a fyne.Theme you can install on your app. |
|
internal
|
|
|
geometry
Package geometry holds the integer-rounded layout math that maps between model positions (line, rune column) and content-space pixels.
|
Package geometry holds the integer-rounded layout math that maps between model positions (line, rune column) and content-space pixels. |
|
parse
Package parse turns raw bytes into a model.Document.
|
Package parse turns raw bytes into a model.Document. |
