~gioverse/chat

ref: c86e075f0df33cc01ac99f167a09f981997727d0 chat/list/synthesizer.go -rw-r--r-- 2.7 KiB
c86e075fChris Waldon debug: add function to easily log structures 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package list

import (
	"fmt"

	"gioui.org/layout"
)

// Synthesis holds the results of transforming a slice of Elements
// with a Synthesizer hook.
type Synthesis struct {
	// Elements holds the resulting elements.
	Elements []Element
	// SerialToIndex maps the serial of an element to the index it
	// occupies within the Elements slice.
	SerialToIndex map[Serial]int
	// ToSourceIndicies maps each index in Elements to the index of
	// the element that generated it when given to the Synthesizer.
	// It is always true that Elements[i] was synthesized from
	// Source[ToSourceIndicies[i]].
	ToSourceIndicies []int
	// The source elements.
	Source []Element
}

func (s Synthesis) String() string {
	return fmt.Sprintf("{Elements: %v}", s.Elements)
}

// SerialAt returns the serial at the given index within the Source slice,
// if there is one.
func (s Synthesis) SerialAt(index int) Serial {
	if index < 0 || index >= len(s.Source) {
		return NoSerial
	}
	return s.Source[index].Serial()
}

// ViewportToSerials converts the First and Count fields of the provided
// viewport into a pair of serials representing the range of elements
// visible within that viewport.
func (s Synthesis) ViewportToSerials(viewport layout.Position) (Serial, Serial) {
	if len(s.ToSourceIndicies) < 1 {
		return NoSerial, NoSerial
	}
	if viewport.First >= len(s.ToSourceIndicies) {
		viewport.First = len(s.ToSourceIndicies) - 1
	} else if viewport.First < 0 {
		viewport.First = 0
	}
	startSrcIdx := s.ToSourceIndicies[viewport.First]
	startSerial := SerialAtOrBefore(s.Source, startSrcIdx)
	lastIndex := len(s.ToSourceIndicies) - 1
	vpLastIndex := max(0, viewport.First+viewport.Count-1)
	endSrcIdx := s.ToSourceIndicies[min(vpLastIndex, lastIndex)]
	endSerial := SerialAtOrAfter(s.Source, endSrcIdx)
	return startSerial, endSerial
}

// Synthesize applies a Synthesizer to a slice of elements, returning
// the resulting slice of elements as well as a mapping from the index
// of each resulting element to the input element that generated it.
func Synthesize(elements []Element, synth Synthesizer) Synthesis {
	var s Synthesis
	s.Source = elements
	for i, elem := range elements {
		var (
			previous Element
			next     Element
		)
		if i > 0 {
			previous = elements[i-1]
		} else {
			previous = Start{}
		}
		if i < len(elements)-1 {
			next = elements[i+1]
		} else {
			next = End{}
		}
		synthesized := synth(previous, elem, next)
		// Mark that each of these synthesized elements came from the
		// raw element at index i.
		for range synthesized {
			s.ToSourceIndicies = append(s.ToSourceIndicies, i)
		}
		s.Elements = append(s.Elements, synthesized...)
	}
	s.SerialToIndex = make(map[Serial]int)
	for i, e := range s.Elements {
		if e.Serial() != NoSerial {
			s.SerialToIndex[e.Serial()] = i
		}
	}
	return s
}