X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/7b6d0a81e0f3c8e33b1b76d5ecbc5bdb25a87249..c781f79f3c8cb9a7577ee1574d196988da2719b3:/view.go diff --git a/view.go b/view.go index e392364..1b3ed6a 100644 --- a/view.go +++ b/view.go @@ -23,42 +23,6 @@ const ( viewPending = "(not summarized yet: zoom it)" ) -// newView returns an empty view with the limits high and low. -func newView(high, low int) *View { - return &View{ - high: high, - low: low, - nodes: make(map[nodeKey]Node), - } -} - -// restoreView returns the view of state with the limits high and low. Each line -// must continue the one before it, and each line above level 0 must be built. -func restoreView(state ViewState, high, low int, tree ViewNodeReader) (*View, error) { - v := newView(high, low) - v.merging = state.Merging - for _, pair := range state.Lines { - line := nodeKey{pair[0], pair[1]} - if line.level < 0 || line.level >= bits.UintSize-2 || line.index < 0 || line.index< 0 && !built { - return nil, fmt.Errorf("view line (%d, %d) is not built", line.level, line.index) - } - - v.lines = append(v.lines, line) - v.end = nodeEnd(line.level, line.index) - } - - return v, nil -} - // View is a list of tree nodes that covers messages 0 to end-1 without a gap, // oldest first. Each new message appends its leaf as a line. Once the size of // the view goes above high, one batch merges the most due sibling pairs until @@ -77,12 +41,33 @@ func restoreView(state ViewState, high, low int, tree ViewNodeReader) (*View, er type View struct { high int low int - lines []nodeKey - nodes map[nodeKey]Node + lines []NodeKey + nodes map[NodeKey]Node end int merging bool } +// ViewState is the saved form of a View. The limits are not part of it. +type ViewState struct { + Lines [][2]int `json:"lines"` + Merging bool `json:"merging"` +} + +// ViewNodeReader gives the built nodes of a tree. +type ViewNodeReader interface { + Built(level, index int) (bool, error) + Node(level, index int) (Node, bool, error) +} + +// newView returns an empty view with the limits high and low. +func newView(high, low int) *View { + return &View{ + high: high, + low: low, + nodes: make(map[NodeKey]Node), + } +} + // State returns the saved form of the view. func (v *View) State() ViewState { state := ViewState{ @@ -100,7 +85,7 @@ func (v *View) State() ViewState { // Append adds the leaf of the next message as a line and merges when the view // is too large. It tells if a merge changed the view. func (v *View) Append(tree ViewNodeReader) (bool, error) { - line := nodeKey{0, v.end} + line := NodeKey{0, v.end} _, err := v.load(tree, line) if err != nil { return false, err @@ -118,8 +103,8 @@ func (v *View) Append(tree ViewNodeReader) (bool, error) { // either nest or do not overlap, so the result covers the messages without a // gap. func (v *View) Coarsen(other *View, tree ViewNodeReader) error { - lines := make([]nodeKey, 0, len(v.lines)) - nodes := make(map[nodeKey]Node, len(v.nodes)) + lines := make([]NodeKey, 0, len(v.lines)) + nodes := make(map[NodeKey]Node, len(v.nodes)) mine, theirs := 0, 0 for start := 0; start < v.end; { line := v.lines[mine] @@ -154,7 +139,7 @@ func (v *View) Coarsen(other *View, tree ViewNodeReader) error { // Refresh keeps node when it is the node of a pending line. func (v *View) Refresh(node Node) { - key := nodeKey{node.Level, node.Index} + key := NodeKey{node.Level, node.Index} for n := len(v.lines) - 1; n >= 0; n-- { line := v.lines[n] if line == key { @@ -168,6 +153,18 @@ func (v *View) Refresh(node Node) { } } +// Pending tells if a line of the view is not built yet. +func (v *View) Pending() bool { + for _, line := range v.lines { + _, built := v.nodes[line] + if !built { + return true + } + } + + return false +} + // Render returns the view as the model reads it: one line "id+n|text" for each // node inside tags, with the newlines of the text as spaces. func (v *View) Render() string { @@ -185,7 +182,9 @@ func (v *View) LinesWithout(skip map[int]bool) []string { } var text strings.Builder + v.renderLine(&text, line) + lines = append(lines, text.String()) } @@ -197,7 +196,9 @@ func (v *View) LinesWithout(skip map[int]bool) []string { // sees a pending mark or a part of a message. func (v *View) Context(end int) string { var text strings.Builder + text.WriteString("\n") + for _, line := range v.lines { _, built := v.nodes[line] if !built || nodeEnd(line.level, line.index) > end { @@ -213,7 +214,7 @@ func (v *View) Context(end int) string { } // load keeps the node of line when it is built, and tells if it is. -func (v *View) load(tree ViewNodeReader, line nodeKey) (bool, error) { +func (v *View) load(tree ViewNodeReader, line NodeKey) (bool, error) { node, built, err := tree.Node(line.level, line.index) if err != nil || !built { return false, err @@ -240,7 +241,7 @@ func (v *View) fit(tree ViewNodeReader) (bool, error) { } left, right := v.lines[best], v.lines[best+1] - parent := nodeKey{left.level + 1, left.index / 2} + parent := NodeKey{left.level + 1, left.index / 2} _, err = v.load(tree, parent) if err != nil { return merged, err @@ -249,6 +250,7 @@ func (v *View) fit(tree ViewNodeReader) (bool, error) { size += v.lineSize(parent) - v.lineSize(left) - v.lineSize(right) delete(v.nodes, left) delete(v.nodes, right) + v.lines[best] = parent v.lines = append(v.lines[:best+1], v.lines[best+2:]...) merged = true @@ -260,7 +262,7 @@ func (v *View) fit(tree ViewNodeReader) (bool, error) { } // renderLine writes one view line, "id+n|text", and a newline. -func (v *View) renderLine(text *strings.Builder, line nodeKey) { +func (v *View) renderLine(text *strings.Builder, line NodeKey) { body := viewPending node, built := v.nodes[line] if built { @@ -304,7 +306,7 @@ func (v *View) mostDue(tree ViewNodeReader) (int, error) { built, err := tree.Built(left.level+1, left.index/2) if err != nil { - return -1, err + return -1, fmt.Errorf("check parent node: %w", err) } if built { @@ -317,7 +319,7 @@ func (v *View) mostDue(tree ViewNodeReader) (int, error) { } // lineSize returns the size of a view line. -func (v *View) lineSize(line nodeKey) int { +func (v *View) lineSize(line NodeKey) int { node, built := v.nodes[line] if !built { return len(viewPending) @@ -326,14 +328,29 @@ func (v *View) lineSize(line nodeKey) int { return node.Size } -// ViewState is the saved form of a View. The limits are not part of it. -type ViewState struct { - Lines [][2]int `json:"lines"` - Merging bool `json:"merging"` -} +// restoreView returns the view of state with the limits high and low. Each line +// must continue the one before it, and each line above level 0 must be built. +func restoreView(state ViewState, high, low int, tree ViewNodeReader) (*View, error) { + v := newView(high, low) + v.merging = state.Merging + for _, pair := range state.Lines { + line := NodeKey{pair[0], pair[1]} + if line.level < 0 || line.level >= bits.UintSize-2 || line.index < 0 || line.index< 0 && !built { + return nil, fmt.Errorf("view line (%d, %d) is not built", line.level, line.index) + } + + v.lines = append(v.lines, line) + v.end = nodeEnd(line.level, line.index) + } + + return v, nil }