turbo-editors/turbo-corepublic Fork 0
v0.9.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

selection.go · 126 lines · 3.6 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 19h ago1package buffer
2
3// StartSelection drops the selection anchor where the cursor is. Moving the
4// cursor afterwards extends the selection from that anchor, which is what
5// holding Shift does.
6//
7// b := buffer.NewFromString("hello")
8// b.StartSelection()
9// b.MoveWordRight()
10// fmt.Println(b.SelectedText()) // hello
11func (b *Buffer) StartSelection() {
12 anchor := b.cursor
13 b.anchor = &anchor
14}
15
16// ClearSelection forgets the anchor, leaving nothing selected.
17func (b *Buffer) ClearSelection() {
18 b.anchor = nil
19}
20
21// SelectAll selects the whole buffer and puts the cursor at its end.
22func (b *Buffer) SelectAll() {
23 b.MoveBufferStart()
24 b.Extend(b.MoveBufferEnd)
25}
26
27// Extend runs a cursor movement while keeping the selection anchor, which is
28// exactly what holding Shift does. If nothing was selected yet, the anchor is
29// dropped where the cursor stands before the move.
30//
31// Every movement method works here, so there is no Shift-flavoured twin of
32// each of them:
33//
34// b.Extend(b.MoveWordRight)
35// b.Extend(b.MoveDown)
36func (b *Buffer) Extend(move func()) {
37 if b.anchor == nil {
38 b.StartSelection()
39 }
40 anchor := b.anchor
41 move()
42 b.anchor = anchor
43}
44
45// SetCursorKeepingSelection moves the cursor to p while keeping the selection
46// anchor. It is Extend for an absolute position, which is what a mouse drag
47// needs; SetCursor is the plain, selection-clearing move.
48func (b *Buffer) SetCursorKeepingSelection(p Position) {
49 b.Extend(func() { b.SetCursor(p) })
50}
51
52// Selection returns the selected span and whether there is one. A selection
53// that has been reduced to nothing by moving the cursor back onto the anchor
54// counts as absent.
55func (b *Buffer) Selection() (Range, bool) {
56 if b.anchor == nil {
57 return Range{}, false
58 }
59 r := NewRange(*b.anchor, b.cursor)
60 if r.IsEmpty() {
61 return Range{}, false
62 }
63 return r, true
64}
65
66// SelectedText returns the selected text, or the empty string when nothing is
67// selected. Lines are joined with line feeds regardless of the file's own line
68// endings, because this is what goes on the clipboard.
69func (b *Buffer) SelectedText() string {
70 r, ok := b.Selection()
71 if !ok {
72 return ""
73 }
74 return joinRunes(b.textIn(r))
75}
76
77// DeleteSelection removes the selected text and reports whether there was any.
78// It is the first thing every editing operation does, so that typing over a
79// selection replaces it.
80func (b *Buffer) DeleteSelection() bool {
81 r, ok := b.Selection()
82 if !ok {
83 b.ClearSelection()
84 return false
85 }
86 b.ClearSelection()
87 b.DeleteRange(r)
88 return true
89}
90
91// SelectWord selects the word at a position and puts the cursor at its end.
92//
93// The word is a run of the runes IsWordRune accepts — the same notion
94// Ctrl-Left and Ctrl-Right move by, and the same one the completion popup uses
95// to work out what is being typed. One editor, one idea of a word.
96//
97// b := buffer.NewFromString("func main() {}")
98// b.SelectWord(buffer.Position{Line: 0, Col: 6})
99// r, _ := b.Selection() // covers "main"
100//
101// A position that is not on a word rune — a space, a bracket, past the end of
102// a line — selects nothing and only moves the cursor. Editors disagree about
103// what a run of punctuation means, and answering nothing is at least an answer
104// that can be predicted.
105func (b *Buffer) SelectWord(p Position) {
106 p = b.clamp(p)
107 line := b.lines[p.Line]
108
109 if p.Col >= len(line) || !IsWordRune(line[p.Col]) {
110 b.ClearSelection()
111 b.SetCursor(p)
112 return
113 }
114
115 start, end := p.Col, p.Col
116 for start > 0 && IsWordRune(line[start-1]) {
117 start--
118 }
119 for end < len(line) && IsWordRune(line[end]) {
120 end++
121 }
122
123 b.SetCursor(Position{Line: p.Line, Col: start})
124 b.StartSelection()
125 b.SetCursorKeepingSelection(Position{Line: p.Line, Col: end})
126}