Split into category packages; module github.com/ZacharyZhang-NY/MujicaUI
This commit is contained in:
1822 files changed
+24568
-21314
No files matched your search
@@ -0,0 +1,142 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Mention is a whole "@Name" token in a MentionInput's text, at rune offsets [Start, End).
|
||||
type Mention struct {
|
||||
ID string
|
||||
Name string
|
||||
Start, End int
|
||||
}
|
||||
|
||||
// mentionDoc is a MentionInput's text, mentions, caret and selection anchor, in runes.
|
||||
type mentionDoc struct {
|
||||
text []rune
|
||||
mentions []Mention
|
||||
caret, anchor int
|
||||
}
|
||||
|
||||
func (d mentionDoc) clone() mentionDoc {
|
||||
d.text = slices.Clone(d.text)
|
||||
d.mentions = slices.Clone(d.mentions)
|
||||
return d
|
||||
}
|
||||
|
||||
// span returns the selection, widened to whole mentions it touches.
|
||||
func (d *mentionDoc) span() (int, int) {
|
||||
a, b := min(d.caret, d.anchor), max(d.caret, d.anchor)
|
||||
for _, m := range d.mentions {
|
||||
if m.Start < b && m.End > a {
|
||||
a, b = min(a, m.Start), max(b, m.End)
|
||||
}
|
||||
}
|
||||
return a, b
|
||||
}
|
||||
|
||||
// replace puts ins in place of [a, b), dropping mentions inside and shifting later ones; the caret lands after ins.
|
||||
func (d *mentionDoc) replace(a, b int, ins []rune) {
|
||||
d.text = slices.Concat(d.text[:a], ins, d.text[b:])
|
||||
shift := len(ins) - (b - a)
|
||||
kept := d.mentions[:0]
|
||||
for _, m := range d.mentions {
|
||||
switch {
|
||||
case m.End <= a:
|
||||
kept = append(kept, m)
|
||||
case m.Start >= b:
|
||||
m.Start, m.End = m.Start+shift, m.End+shift
|
||||
kept = append(kept, m)
|
||||
}
|
||||
}
|
||||
d.mentions = kept
|
||||
d.caret = a + len(ins)
|
||||
d.anchor = d.caret
|
||||
}
|
||||
|
||||
// insert types s at the caret, over any selection.
|
||||
func (d *mentionDoc) insert(s string) {
|
||||
a, b := d.span()
|
||||
d.replace(a, b, []rune(s))
|
||||
}
|
||||
|
||||
// erase deletes the selection, else one rune or one whole mention back (or forward).
|
||||
func (d *mentionDoc) erase(forward bool) bool {
|
||||
if d.caret != d.anchor {
|
||||
a, b := d.span()
|
||||
d.replace(a, b, nil)
|
||||
return true
|
||||
}
|
||||
for _, m := range d.mentions {
|
||||
if !forward && m.End == d.caret || forward && m.Start == d.caret {
|
||||
d.replace(m.Start, m.End, nil)
|
||||
return true
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case !forward && d.caret > 0:
|
||||
d.replace(d.caret-1, d.caret, nil)
|
||||
case forward && d.caret < len(d.text):
|
||||
d.replace(d.caret, d.caret+1, nil)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// move steps the caret by dir runes, over whole mentions; extend keeps the anchor.
|
||||
func (d *mentionDoc) move(dir int, extend bool) {
|
||||
at := max(0, min(len(d.text), d.caret+dir))
|
||||
for _, m := range d.mentions {
|
||||
if at > m.Start && at < m.End {
|
||||
if dir < 0 {
|
||||
at = m.Start
|
||||
} else {
|
||||
at = m.End
|
||||
}
|
||||
}
|
||||
}
|
||||
d.moveTo(at, extend)
|
||||
}
|
||||
|
||||
func (d *mentionDoc) moveTo(at int, extend bool) {
|
||||
d.caret = at
|
||||
if !extend {
|
||||
d.anchor = at
|
||||
}
|
||||
}
|
||||
|
||||
// query returns where an "@" being typed starts and what follows it, or -1.
|
||||
func (d *mentionDoc) query() (int, string) {
|
||||
if d.caret != d.anchor {
|
||||
return -1, ""
|
||||
}
|
||||
for i := d.caret - 1; i >= 0; i-- {
|
||||
r := d.text[i]
|
||||
if unicode.IsSpace(r) {
|
||||
return -1, ""
|
||||
}
|
||||
if r != '@' {
|
||||
continue
|
||||
}
|
||||
for _, m := range d.mentions {
|
||||
if i >= m.Start && i < m.End {
|
||||
return -1, ""
|
||||
}
|
||||
}
|
||||
if i > 0 && !unicode.IsSpace(d.text[i-1]) {
|
||||
return -1, ""
|
||||
}
|
||||
return i, string(d.text[i+1 : d.caret])
|
||||
}
|
||||
return -1, ""
|
||||
}
|
||||
|
||||
// choose turns the "@query" before the caret into a mention of id, followed by a space.
|
||||
func (d *mentionDoc) choose(at int, id, name string) {
|
||||
tok := []rune("@" + name)
|
||||
d.replace(at, d.caret, append(slices.Clone(tok), ' '))
|
||||
d.mentions = append(d.mentions, Mention{ID: id, Name: name, Start: at, End: at + len(tok)})
|
||||
slices.SortFunc(d.mentions, func(a, b Mention) int { return a.Start - b.Start })
|
||||
}
|
||||
Reference in new issue
Block a user