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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
package editor
import (
"strings"
"testing"
"codeberg.org/turbo-editors/turbo-core/buffer"
)
// viewOn returns a view on a buffer holding text, with the cursor placed.
func viewOn(t *testing.T, text string, line, col int) *View {
t.Helper()
buf := buffer.New()
buf.SetText(text)
v := NewView(buf, &Clipboard{})
buf.SetCursor(buffer.Position{Line: line, Col: col})
return v
}
func TestInsertSnippetPutsASingleLineAtTheCursor(t *testing.T) {
v := viewOn(t, "abcd\n", 0, 2)
v.InsertSnippet("XY")
if got := v.Buffer().Text(); got != "abXYcd\n" {
t.Errorf("the buffer holds %q", got)
}
}
func TestInsertSnippetIndentsTheLinesAfterTheFirst(t *testing.T) {
// The case that decides the whole feature: a multi-line snippet dropped
// into indented code restarts at column zero without this.
v := viewOn(t, "func f() {\n\t\n}\n", 1, 1)
v.InsertSnippet("if err != nil {\n\treturn err\n}")
want := "func f() {\n\tif err != nil {\n\t\treturn err\n\t}\n}\n"
if got := v.Buffer().Text(); got != want {
t.Errorf("the buffer holds\n%q\nwant\n%q", got, want)
}
}
func TestInsertSnippetUsesSpacesWhenTheLineDoes(t *testing.T) {
v := viewOn(t, "x = [\n \n]\n", 1, 4)
v.InsertSnippet("a,\nb,")
want := "x = [\n a,\n b,\n]\n"
if got := v.Buffer().Text(); got != want {
t.Errorf("the buffer holds\n%q\nwant\n%q", got, want)
}
}
func TestInsertSnippetAtColumnZeroAddsNoIndent(t *testing.T) {
v := viewOn(t, "\n", 0, 0)
v.InsertSnippet("a\nb\nc")
if got := v.Buffer().Text(); got != "a\nb\nc\n" {
t.Errorf("the buffer holds %q", got)
}
}
func TestInsertSnippetLeavesBlankLinesBlank(t *testing.T) {
// Trailing whitespace on an empty line is something every formatter then
// removes, so putting it there is noise in the next diff.
v := viewOn(t, "func f() {\n\t\n}\n", 1, 1)
v.InsertSnippet("a\n\nb")
want := "func f() {\n\ta\n\n\tb\n}\n"
if got := v.Buffer().Text(); got != want {
t.Errorf("the buffer holds\n%q\nwant\n%q", got, want)
}
}
func TestInsertSnippetIsOneUndoStep(t *testing.T) {
v := viewOn(t, "func f() {\n\t\n}\n", 1, 1)
before := v.Buffer().Text()
v.InsertSnippet("if err != nil {\n\treturn err\n}")
v.Buffer().Undo()
if got := v.Buffer().Text(); got != before {
t.Errorf("one undo left\n%q\nwant\n%q", got, before)
}
}
func TestInsertSnippetLeavesTheCursorAfterTheText(t *testing.T) {
v := viewOn(t, "func f() {\n\t\n}\n", 1, 1)
v.InsertSnippet("a\nb")
cursor := v.Buffer().Cursor()
if cursor.Line != 2 {
t.Errorf("the cursor is on line %d, want the last line of the snippet", cursor.Line)
}
if got := v.Buffer().Line(cursor.Line); cursor.Col != len([]rune(got)) {
t.Errorf("the cursor is at column %d of %q, want the end", cursor.Col, got)
}
}
func TestInsertSnippetCountsAsAChange(t *testing.T) {
v := viewOn(t, "x\n", 0, 0)
before := v.Buffer().Revision()
v.InsertSnippet("y")
if v.Buffer().Revision() == before {
t.Error("inserting a snippet did not move the revision, so nothing will re-colour")
}
if !v.Buffer().Modified() {
t.Error("inserting a snippet left the buffer unmodified")
}
}
func TestInsertingAnEmptySnippetChangesNothing(t *testing.T) {
// The revision is what says nothing happened; Modified() cannot, because
// putting the text into the buffer in the first place already set it.
v := viewOn(t, "x\n", 0, 0)
before := v.Buffer().Revision()
v.InsertSnippet("")
if got := v.Buffer().Text(); got != "x\n" {
t.Errorf("the buffer holds %q", got)
}
if v.Buffer().Revision() != before {
t.Error("an empty snippet moved the revision")
}
}
func TestInsertSnippetHandlesAccentsInTheCurrentLine(t *testing.T) {
// The indent is taken as a byte prefix of the line, so a line whose text
// has accents must still give back only its whitespace.
if got := leadingWhitespace("\t évidemment"); got != "\t " {
t.Errorf("leadingWhitespace() = %q, want the tab and two spaces", got)
}
}
func TestLeadingWhitespaceOfABlankLineIsTheWholeLine(t *testing.T) {
if got := leadingWhitespace("\t\t"); got != "\t\t" {
t.Errorf("leadingWhitespace() = %q", got)
}
if got := leadingWhitespace(""); got != "" {
t.Errorf("leadingWhitespace() = %q", got)
}
}
func TestIndentContinuationLinesLeavesASingleLineAlone(t *testing.T) {
if got := indentContinuationLines("only", "\t"); got != "only" {
t.Errorf("got %q, want the text unchanged", got)
}
if strings.Contains(indentContinuationLines("only", "\t"), "\t") {
t.Error("a single-line snippet was indented")
}
}
|