frame.go RawHistoryFolder
1package ansi
2
3import (
4 "fmt"
5 "reflect"
6
7 "github.com/bnema/vev-vt/core"
8)
9
10// Frame is an alias for the frontend-neutral terminal grid. ANSI rendering
11// consumes it but does not own its model or storage policy.
12type Frame = core.Frame
13
14// CellSource is the read-only semantic grid consumed by ANSI rendering.
15type CellSource = core.CellSource
16
17var NewFrame = core.NewFrame
18
19func validateCellSource(source CellSource) error {
20 if source == nil {
21 return fmt.Errorf("nil cell source")
22 }
23 value := reflect.ValueOf(source)
24 switch value.Kind() {
25 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
26 if value.IsNil() {
27 return fmt.Errorf("nil cell source")
28 }
29 }
30 if source.Columns() <= 0 || source.Rows() <= 0 {
31 return fmt.Errorf("invalid cell source size %dx%d", source.Columns(), source.Rows())
32 }
33 if frame, ok := asFrame(source); ok {
34 return frame.Validate()
35 }
36 return nil
37}
38
39// snapshotCellSource returns an independent Frame holding source's cells. When
40// scratch is non-nil the snapshot is written into scratch's existing storage
41// (allocating only to grow or on first use) and the returned Frame shares that
42// storage; the caller owns scratch and must not hand it to anyone else while
43// the snapshot is live. A nil scratch allocates a fresh frame.
44func snapshotCellSource(scratch *Frame, source CellSource) Frame {
45 if scratch == nil {
46 return cloneCellSource(source)
47 }
48 if frame, ok := asFrame(source); ok {
49 scratch.CopyFrom(frame)
50 return *scratch
51 }
52 columns, rows := source.Columns(), source.Rows()
53 if scratch.Width != columns || scratch.Height != rows || scratch.Validate() != nil {
54 *scratch = NewFrame(columns, rows)
55 }
56 for y := range rows {
57 for x := range columns {
58 scratch.Set(x, y, source.Cell(x, y))
59 }
60 }
61 return *scratch
62}
63
64// asFrame reports whether source is a compact core.Frame, directly or through
65// a non-nil pointer, enabling stored-cell paths that bypass CellSource.
66func asFrame(source CellSource) (Frame, bool) {
67 switch frame := source.(type) {
68 case Frame:
69 return frame, true
70 case *Frame:
71 if frame != nil {
72 return *frame, true
73 }
74 }
75 return Frame{}, false
76}
77
78func cloneCellSource(source CellSource) Frame {
79 if frame, ok := asFrame(source); ok {
80 return frame.Clone()
81 }
82 clone := NewFrame(source.Columns(), source.Rows())
83 for y := range source.Rows() {
84 for x := range source.Columns() {
85 clone.Set(x, y, source.Cell(x, y))
86 }
87 }
88 return clone
89}