Skip to content

Commit 1ed139a

Browse files
reworking react
1 parent 5a63b10 commit 1ed139a

15 files changed

Lines changed: 1145 additions & 1009 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package common
2+
3+
// CodeBoxWrapper represents a code box that allows getting and setting
4+
// the code and selection, as well as emitting events.
5+
//
6+
// This interface allows the code box implementation to be replaced
7+
// with a mock implementation for testing.
8+
type CodeBoxWrapper interface {
9+
Code() string
10+
GetSelection() Selection
11+
SetCode(sel Selection, code string)
12+
EmitEvent(event Event)
13+
}
14+
15+
type Event string
16+
17+
const (
18+
EscapeEvent Event = `escape`
19+
SaveEvent Event = `save`
20+
UndoEvent Event = `undo`
21+
RedoEvent Event = `redo`
22+
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package common
2+
3+
// Selection represents a text selection in the code editor.
4+
//
5+
// The selection is defined by the start and end byte offsets in the code string.
6+
// The start and end offsets are inclusive of the start and exclusive of the end.
7+
//
8+
// Typically the start is less than or equal to the end even with a reverse selection,
9+
// since the CodeBoxWrapper should normalize the selection before returning it and
10+
// should keep the same selection direction when setting a new selection.
11+
type Selection struct {
12+
Start int
13+
End int
14+
}
15+
16+
// IsCaret returns true if the selection represents a caret position
17+
// (i.e. start and end are the same).
18+
// See https://en.wikipedia.org/wiki/Caret_navigation
19+
func (sel Selection) IsCaret() bool {
20+
return sel.Start == sel.End
21+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package common
2+
3+
type UndoRedoStack interface {
4+
PerformUndo(cb CodeBoxWrapper)
5+
6+
PerformRedo(cb CodeBoxWrapper)
7+
8+
// AddBreak makes any prior changes not joined into any future changes.
9+
//
10+
// This should be called when the user makes a change and there is an
11+
// automatic code modification such as auto-formatting or auto-completion.
12+
// This allows the user to unto the autmatic change separately from their
13+
// own change.
14+
//
15+
// TODO(grantnelson-wf): Insert AddBreak into editor where appropriate.
16+
AddBreak()
17+
18+
// RecordSelectionChange records a selection change without a code change
19+
// and adds a break to prevent joining with prior changes.
20+
RecordSelectionChange(cb CodeBoxWrapper)
21+
22+
RecordCodeChange(cb CodeBoxWrapper, priorCode string)
23+
}

playground/internal/editor/editor.go

Lines changed: 26 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,18 @@ package editor
66
import (
77
"strings"
88
"unicode"
9+
10+
"github.com/gopherjs/gopherjs.github.io/playground/internal/common"
911
)
1012

1113
const (
1214
// TabWidth is the number of spaces per tab.
1315
TabWidth = 4
1416

15-
EscapeEvent = `escape`
16-
SaveEvent = `save`
17-
UndoEvent = `undo`
18-
RedoEvent = `redo`
19-
2017
commentPrefix = `// `
2118
commentPrefixLen = len(commentPrefix)
2219
)
2320

24-
type CodeBoxWrapper interface {
25-
Code() string
26-
GetSelection() Selection
27-
SetCode(sel Selection, code string)
28-
EmitEvent(event string)
29-
}
30-
31-
type Selection struct {
32-
Start int
33-
End int
34-
}
35-
36-
// IsCaret returns true if the selection represents a caret position
37-
// (i.e. start and end are the same).
38-
// See https://en.wikipedia.org/wiki/Caret_navigation
39-
func (sel Selection) IsCaret() bool {
40-
return sel.Start == sel.End
41-
}
42-
4321
// MeasureLineLength returns the length of the line.
4422
// This counts tabs as multiple spaces so that the visual horizontal offset
4523
// is correct, assuming a monospace font.
@@ -84,12 +62,12 @@ func LongestMeasuredLineLength(code string) int {
8462
return longest
8563
}
8664

87-
func ProcessKeyDown(wrapper CodeBoxWrapper, key string, shift, ctrl bool) bool {
65+
func ProcessKeyDown(wrapper common.CodeBoxWrapper, key string, shift, ctrl bool) bool {
8866
ce := &codeEditor{CodeBoxWrapper: wrapper}
8967
return ce.handleKeyDown(key, shift, ctrl)
9068
}
9169

92-
type codeEditor struct{ CodeBoxWrapper }
70+
type codeEditor struct{ common.CodeBoxWrapper }
9371

9472
func (ce *codeEditor) handleKeyDown(key string, shift, ctrl bool) bool {
9573
switch key {
@@ -184,7 +162,7 @@ func (ce *codeEditor) handleSpace(shift, ctrl bool) bool {
184162
write(newCode, code[caret:])
185163

186164
caret = lineStart + tabs + spaces
187-
newSel := Selection{Start: caret, End: caret}
165+
newSel := common.Selection{Start: caret, End: caret}
188166
ce.SetCode(newSel, newCode.String())
189167
return true
190168
}
@@ -215,7 +193,7 @@ func (ce *codeEditor) handleTab(shift, ctrl bool) bool {
215193
func (ce *codeEditor) addIndents() bool {
216194
lineSel := ce.getSelectedLines()
217195
nonBlankLines := 0
218-
ce.foreachLine(lineSel, func(line string, _ Selection) bool {
196+
ce.foreachLine(lineSel, func(line string, _ common.Selection) bool {
219197
if !isBlankLine(line) {
220198
nonBlankLines++
221199
}
@@ -233,7 +211,7 @@ func (ce *codeEditor) addIndents() bool {
233211
newCode.Grow(len(code) + nonBlankLines)
234212

235213
write(newCode, code[:lineSel.Start])
236-
ce.foreachLine(lineSel, func(line string, curLineSel Selection) bool {
214+
ce.foreachLine(lineSel, func(line string, curLineSel common.Selection) bool {
237215
if isBlankLine(line) {
238216
write(newCode, line) // Empty line, just add as-is.
239217
return true
@@ -253,7 +231,7 @@ func (ce *codeEditor) addIndents() bool {
253231
func (ce *codeEditor) removeIndents() bool {
254232
lineSel := ce.getSelectedLines()
255233
indentedLines := 0
256-
ce.foreachLine(lineSel, func(line string, _ Selection) bool {
234+
ce.foreachLine(lineSel, func(line string, _ common.Selection) bool {
257235
if line[0] == '\t' || line[0] == ' ' {
258236
indentedLines++
259237
}
@@ -271,7 +249,7 @@ func (ce *codeEditor) removeIndents() bool {
271249
newCode.Grow(len(code) - indentedLines) // assumes tabs are being removed
272250

273251
write(newCode, code[:lineSel.Start])
274-
ce.foreachLine(lineSel, func(line string, curLineSel Selection) bool {
252+
ce.foreachLine(lineSel, func(line string, curLineSel common.Selection) bool {
275253
// Trim up to one tab or equivalent spaces.
276254
removeChars := 0
277255
for i := 0; i < len(line); i++ {
@@ -367,12 +345,12 @@ func (ce *codeEditor) handleSave(shift, ctrl bool) bool {
367345
return false
368346
}
369347

370-
ce.EmitEvent(SaveEvent)
348+
ce.EmitEvent(common.SaveEvent)
371349
return true
372350
}
373351

374352
func (ce *codeEditor) handleEscape() bool {
375-
ce.EmitEvent(EscapeEvent)
353+
ce.EmitEvent(common.EscapeEvent)
376354
return true
377355
}
378356

@@ -383,11 +361,11 @@ func (ce *codeEditor) handleUndo(shift, ctrl bool) bool {
383361
}
384362

385363
if shift {
386-
ce.EmitEvent(RedoEvent)
364+
ce.EmitEvent(common.RedoEvent)
387365
return true
388366
}
389367

390-
ce.EmitEvent(UndoEvent)
368+
ce.EmitEvent(common.UndoEvent)
391369
return true
392370
}
393371

@@ -397,7 +375,7 @@ func (ce *codeEditor) handleRedo(shift, ctrl bool) bool {
397375
return false
398376
}
399377

400-
ce.EmitEvent(RedoEvent)
378+
ce.EmitEvent(common.RedoEvent)
401379
return true
402380
}
403381

@@ -411,7 +389,7 @@ func (ce *codeEditor) handleCommentToggle(shift, ctrl bool) bool {
411389
lineSel := ce.getSelectedLines()
412390
containsOnlyBlankLines := true
413391
containsUncommentedLine := false
414-
ce.foreachLine(lineSel, func(line string, _ Selection) bool {
392+
ce.foreachLine(lineSel, func(line string, _ common.Selection) bool {
415393
if trimmed := trimLeftSpace(line); trimmed != `` {
416394
containsOnlyBlankLines = false
417395
if !strings.HasPrefix(trimmed, commentPrefix) {
@@ -438,10 +416,10 @@ func (ce *codeEditor) handleCommentToggle(shift, ctrl bool) bool {
438416

439417
// addCommenting will add the comment prefix to each non-blank line in
440418
// the given range including any lines that already are commented.
441-
func (ce *codeEditor) addCommenting(lineSel Selection) {
419+
func (ce *codeEditor) addCommenting(lineSel common.Selection) {
442420
leastIndent := -1
443421
nonBlankLines := 0
444-
ce.foreachLine(lineSel, func(line string, _ Selection) bool {
422+
ce.foreachLine(lineSel, func(line string, _ common.Selection) bool {
445423
if trimmed := trimLeftSpace(line); trimmed != `` {
446424
if indent := len(line) - len(trimmed); leastIndent < 0 || indent < leastIndent {
447425
leastIndent = indent
@@ -465,7 +443,7 @@ func (ce *codeEditor) addCommenting(lineSel Selection) {
465443
newCode.Grow(len(code) + nonBlankLines*commentPrefixLen)
466444

467445
write(newCode, code[:lineSel.Start])
468-
ce.foreachLine(lineSel, func(line string, curLineSel Selection) bool {
446+
ce.foreachLine(lineSel, func(line string, curLineSel common.Selection) bool {
469447
if isBlankLine(line) {
470448
write(newCode, line) // Empty line, just add as-is.
471449
return true
@@ -483,15 +461,15 @@ func (ce *codeEditor) addCommenting(lineSel Selection) {
483461

484462
// removeCommenting will uncomment any line that starts with the comment prefix
485463
// (and preceding whitespace).
486-
func (ce *codeEditor) removeCommenting(lineSel Selection) {
464+
func (ce *codeEditor) removeCommenting(lineSel common.Selection) {
487465
code := ce.Code()
488466
sel := ce.GetSelection()
489467
newSel := sel
490468
newCode := &strings.Builder{}
491469
newCode.Grow(len(code))
492470

493471
write(newCode, code[:lineSel.Start])
494-
ce.foreachLine(lineSel, func(line string, curLineSel Selection) bool {
472+
ce.foreachLine(lineSel, func(line string, curLineSel common.Selection) bool {
495473
if trimmed := trimLeftSpace(line); trimmed != `` {
496474
if index := strings.Index(line, commentPrefix); index >= 0 {
497475
write(newCode, line[:index])
@@ -514,11 +492,11 @@ func (ce *codeEditor) removeCommenting(lineSel Selection) {
514492
// or at the front of all the code.
515493
// The returned end should be after the "\n" after the selection
516494
// or at the end of all the code.
517-
func (ce *codeEditor) getSelectedLines() Selection {
495+
func (ce *codeEditor) getSelectedLines() common.Selection {
518496
code := ce.Code()
519497
codeLen := len(code)
520498
sel := ce.GetSelection()
521-
lineSel := Selection{Start: 0, End: codeLen}
499+
lineSel := common.Selection{Start: 0, End: codeLen}
522500
if inRange(sel.Start, 0, codeLen) {
523501
lineSel.Start = findStartOfLastLine(code[:sel.Start])
524502
}
@@ -542,12 +520,12 @@ func (ce *codeEditor) getSelectedLines() Selection {
542520
//
543521
// If the yield function returns false, iteration stops and
544522
// foreachLine returns false. Otherwise, it returns true.
545-
func (ce *codeEditor) foreachLine(sel Selection, yield func(line string, curLineSel Selection) bool) bool {
523+
func (ce *codeEditor) foreachLine(sel common.Selection, yield func(line string, curLineSel common.Selection) bool) bool {
546524
code := ce.Code()
547525
for i := sel.Start; i < sel.End; {
548526
lineEnd := findEndOfLineAfter(i, code[:sel.End])
549527
line := code[i:lineEnd]
550-
if !yield(line, Selection{i, lineEnd}) {
528+
if !yield(line, common.Selection{Start: i, End: lineEnd}) {
551529
return false
552530
}
553531
i = lineEnd
@@ -568,7 +546,7 @@ func (ce *codeEditor) insertPair(ctrl bool, before, after string) bool {
568546
func (ce *codeEditor) insertAtSelection(before, after string, keepSelection bool) {
569547
code := ce.Code()
570548
sel := ce.GetSelection()
571-
newSel := Selection{Start: sel.Start, End: sel.Start} // set both to start initially
549+
newSel := common.Selection{Start: sel.Start, End: sel.Start} // set both to start initially
572550

573551
beforeLen := len(before)
574552
afterLen := len(after)
@@ -670,7 +648,7 @@ func inRange(value, start, end int) bool {
670648
// if the current selection is after the given limit.
671649
// This moves the selection based on where new text is being inserted at or
672650
// removed from the limit position.
673-
func adjustSel(newSel *Selection, curSel Selection, limit, adjustment int) {
651+
func adjustSel(newSel *common.Selection, curSel common.Selection, limit, adjustment int) {
674652
if curSel.Start > limit {
675653
newSel.Start += adjustment
676654
}

0 commit comments

Comments
 (0)