Skip to content

Commit b12aa11

Browse files
Adding more tests
1 parent a99a7e9 commit b12aa11

File tree

5 files changed

+6298
-3957
lines changed

5 files changed

+6298
-3957
lines changed

playground/internal/cmd/precompile/precompile.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,14 @@ func run() error {
6363
return fmt.Errorf("failed to get build package for %s: %w", path, err)
6464
}
6565

66-
if _, err = s.LoadPackages(pkg); err != nil {
66+
var srcs *sources.Sources
67+
if srcs, err = s.LoadPackages(pkg); err != nil {
6768
return fmt.Errorf("failed to prepackaged package %q: %w", pkg, err)
6869
}
70+
71+
if _, err := s.PrepareAndCompilePackages(srcs); err != nil {
72+
return fmt.Errorf("failed to compile package %q: %w", pkg, err)
73+
}
6974
}
7075

7176
target, err := targetDir(s)

playground/internal/editor/editor.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,15 @@ func (ce *codeEditor) handleMultilineComment(shift, ctrl bool) bool {
309309
return false
310310
}
311311

312+
code := ce.Code()
312313
sel := ce.GetSelection()
313314
if !sel.IsCaret() {
314315
// If a selection, just prerform default behavior.
315316
return false
316317
}
317318
caret := sel.Start
318319

319-
if caret <= 0 || ce.Code()[caret-1] != '/' {
320+
if caret <= 0 || code[caret-1] != '/' {
320321
// Not preceded by '/', allow default behavior.
321322
return false
322323
}
@@ -350,7 +351,7 @@ func (ce *codeEditor) handleNewline(shift, ctrl bool) bool {
350351
if inRange(sel.End, 0, len(code)-1) {
351352
switch code[sel.End] {
352353
case '}', ')', ']':
353-
if opening := ce.findMatchingOpeningBrace(sel.End); opening >= 0 {
354+
if opening := findMatchingOpeningBrace(code, sel.End); opening >= 0 {
354355
after = "\n" + ce.indentAt(opening)
355356
}
356357
}
@@ -626,8 +627,7 @@ func (ce *codeEditor) indentAt(start int) string {
626627
// for the closing brace at the given caret position.
627628
// Currently this does not account for braces inside strings or comments.
628629
// It returns -1 if no matching opening brace is found.
629-
func (ce *codeEditor) findMatchingOpeningBrace(caret int) int {
630-
code := ce.Code()
630+
func findMatchingOpeningBrace(code string, caret int) int {
631631
if caret <= 0 || caret > len(code) {
632632
return -1
633633
}

playground/internal/editor/editor_test.go

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,97 @@ func Test_Editor_ProcessKeyDown_Tab(t *testing.T) {
401401
})
402402
}
403403

404-
// TODO(grantnelson-wf): Add tests for findMatchingOpeningBrace
404+
func Test_Editor_findMatchingOpeningBrace(t *testing.T) {
405+
tests := []struct {
406+
name string
407+
code string
408+
caret int
409+
exp int
410+
}{
411+
{
412+
name: `empty code`,
413+
code: ``,
414+
caret: 0,
415+
exp: -1,
416+
},
417+
{
418+
name: `no braces`,
419+
code: `fmt.Println("Hello")`,
420+
caret: 4, // after `fmt.`
421+
exp: -1,
422+
},
423+
{
424+
name: `adjacent parenthesis`,
425+
code: `func main()`,
426+
caret: 10, // before `)`
427+
exp: 9, // before `(`
428+
},
429+
{
430+
name: `simple parenthesis`,
431+
code: `fmt.Println("Hello")`,
432+
caret: 19, // before `)`
433+
exp: 11, // before `(`
434+
},
435+
{
436+
name: `nested braces`,
437+
code: lines(
438+
`func main() {`,
439+
` if true {`,
440+
` fmt.Println("Hello")`,
441+
` }`,
442+
`}`),
443+
caret: 51, // before last `}` on line 5
444+
exp: 12, // before `{` on line 1
445+
},
446+
{
447+
name: `too many`,
448+
code: `{[()]})`,
449+
caret: 6, // before last `)`
450+
exp: -1, // no matching opening parenthesis
451+
},
452+
{
453+
name: `just right outter`,
454+
code: `({[()]})`,
455+
caret: 7, // before last `)`
456+
exp: 0, // before first `(`
457+
},
458+
{
459+
name: `just right inner`,
460+
code: `({[()]})`,
461+
caret: 5, // before `]`
462+
exp: 2, // before `[`
463+
},
464+
{
465+
name: `mismatched at end`,
466+
code: `[{[()]})`,
467+
caret: 7, // before last `)`
468+
exp: -1, // no matching opening parenthesis
469+
},
470+
{
471+
name: `mismatched in middle`,
472+
code: `({[{)]})`,
473+
caret: 7, // before last `)`
474+
exp: -1, // no matching opening parenthesis
475+
},
476+
{
477+
// This is a known limitation since we don't parse strings.
478+
// Since the brace matching is to help with indents,
479+
// this is acceptable for now. It won't be hit too often in
480+
// practice without unmatched braces in multiple strings.
481+
name: `quotes do not work yet`,
482+
code: `fmt.Println("(")`,
483+
caret: 15, // before `)`
484+
exp: 13, // incorrectly before `(` in quotes
485+
},
486+
}
487+
488+
for _, tt := range tests {
489+
t.Run(tt.name, func(t *testing.T) {
490+
got := findMatchingOpeningBrace(tt.code, tt.caret)
491+
check(t, `position`, got, tt.exp)
492+
})
493+
}
494+
}
405495

406496
func Test_Editor_ProcessKeyDown_Newline(t *testing.T) {
407497
runKeyDownTest(t, testKeyDown{
@@ -693,15 +783,15 @@ func Test_Editor_ProcessKeyDown_SelectedLines(t *testing.T) {
693783
}
694784
ce := &codeEditor{CodeBoxWrapper: cb}
695785
lineSel := ce.getSelectedLines()
696-
check(t, "startLine", lineSel.Start, tt.wantStartLine)
697-
check(t, "endLine", lineSel.End, tt.wantEndLine)
786+
check(t, `startLine`, lineSel.Start, tt.wantStartLine)
787+
check(t, `endLine`, lineSel.End, tt.wantEndLine)
698788

699789
gotSelectedLines := []string{}
700790
ce.foreachLine(lineSel, func(line string, _ Selection) bool {
701791
gotSelectedLines = append(gotSelectedLines, line)
702792
return true
703793
})
704-
check(t, "selectedLines", gotSelectedLines, tt.wantSelectedLines)
794+
check(t, `selectedLines`, gotSelectedLines, tt.wantSelectedLines)
705795
})
706796
}
707797
}
@@ -1224,11 +1314,11 @@ func runKeyDownTest(t *testing.T, tt testKeyDown) {
12241314
initSelectionEnd: tt.selectionEnd,
12251315
}
12261316
gotPreventDefault := ProcessKeyDown(cb, tt.key, tt.shift, tt.ctrl)
1227-
check(t, "code", cb.gotCode, tt.wantCode)
1228-
check(t, "selectedStart", cb.gotSelectionStart, tt.wantSelectionStart)
1229-
check(t, "selectedEnd", cb.gotSelectionEnd, tt.wantSelectionEnd)
1230-
check(t, "preventDefault", gotPreventDefault, tt.wantPrefentDefault)
1231-
check(t, "eventCalls", cb.gotEventCalls, tt.wantEventCalls)
1317+
check(t, `code`, cb.gotCode, tt.wantCode)
1318+
check(t, `selectedStart`, cb.gotSelectionStart, tt.wantSelectionStart)
1319+
check(t, `selectedEnd`, cb.gotSelectionEnd, tt.wantSelectionEnd)
1320+
check(t, `preventDefault`, gotPreventDefault, tt.wantPrefentDefault)
1321+
check(t, `eventCalls`, cb.gotEventCalls, tt.wantEventCalls)
12321322
})
12331323
}
12341324

playground/internal/editor/undo.go

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,111 @@
11
package editor
22

3+
import "time"
4+
5+
const undoRedoJoinDuration = time.Second
6+
7+
type undoRedoState struct {
8+
prefix int
9+
suffix int
10+
oldCode string
11+
newCode string
12+
oldSel Selection
13+
newSel Selection
14+
}
15+
16+
type UndoRedoStack struct {
17+
undos []*undoRedoState
18+
redos []*undoRedoState
19+
getTime func() time.Time
20+
21+
lastChange time.Time
22+
lastSel Selection
23+
}
24+
25+
func NewUndoRedoStack() *UndoRedoStack {
26+
return &UndoRedoStack{getTime: time.Now}
27+
}
28+
29+
func (s *UndoRedoStack) PerformUndo(cb CodeBoxWrapper) {
30+
// TODO(grantnelson-wf): Finish implmementing
31+
}
32+
33+
func (s *UndoRedoStack) PerformRedo(cb CodeBoxWrapper) {
34+
// TODO(grantnelson-wf): Finish implmementing
35+
}
36+
37+
func (s *UndoRedoStack) RecordSelectionChange(cb CodeBoxWrapper) {
38+
// TODO(grantnelson-wf): Finish implmementing
39+
40+
}
41+
42+
func (s *UndoRedoStack) RecordCodeChange(cb CodeBoxWrapper, priorCode string) {
43+
//now := s.getTime()
44+
45+
// TODO(grantnelson-wf): Finish implmementing
46+
47+
s.redos = nil // clear redo stack on new code change
48+
}
49+
350
/*
4-
type undoState struct {
5-
code string
51+
func (s *UndoRedoStack) joinStates(older, newer *undoRedoState) bool {
52+
if newer.start.Sub(older.start) > undoRedoJoinDuration {
53+
return false // changes happened too far apart in time to join
54+
}
55+
56+
if older.prefix > newer.prefix+len(newer.newCode) ||
57+
older.suffix > newer.suffix+len(newer.newCode) {
58+
return false // changes don't overlap so are too far apart in code to join
59+
}
660
7-
beforeStartSel int
8-
beforeStopSel int
9-
afterStartSel int
10-
afterStopSel int
61+
// TODO(grantnelson-wf): Finish implmementing
62+
63+
return true
64+
}
65+
66+
func newState(priorCode, newCode string, priorSel, afterSel Selection, start time.Time) *undoRedoState {
67+
prefixLen, suffixLen := diffTrim(priorCode, newCode)
68+
return &undoRedoState{
69+
prefix: prefixLen,
70+
suffix: suffixLen,
71+
newCode: newCode[prefixLen : len(newCode)-suffixLen],
72+
prior: priorSel,
73+
after: afterSel,
74+
start: start,
75+
}
1176
}
1277
*/
78+
79+
// diffTrim returns the lengths of the common prefix and suffix between
80+
// prior and after strings.
81+
func diffTrim(prior, after string) (int, int) {
82+
priorLen, afterLen := len(prior), len(after)
83+
minLen := min(priorLen, afterLen)
84+
85+
prefixLen := 0
86+
for prefixLen < minLen && prior[prefixLen] == after[prefixLen] {
87+
prefixLen++
88+
}
89+
if prefixLen >= minLen {
90+
return prefixLen, 0
91+
}
92+
93+
suffixLen := 0
94+
minLen -= prefixLen
95+
priorMax, afterMax := priorLen-1, afterLen-1
96+
for suffixLen < minLen && prior[priorMax] == after[afterMax] {
97+
suffixLen++
98+
priorMax--
99+
afterMax--
100+
}
101+
return prefixLen, suffixLen
102+
}
103+
104+
// TODO(grantnelson-wf): Remove when `min` is available in go1.21.
105+
// See https://pkg.go.dev/builtin#min
106+
func min(a, b int) int {
107+
if a < b {
108+
return a
109+
}
110+
return b
111+
}

0 commit comments

Comments
 (0)