@@ -29,22 +29,28 @@ func codeBoxComponent(props Props) *Element {
2929 lineNumsRef = UseRef ()
3030 )
3131
32- onInput := UseCallback (func (e * js.Object ) {
33- setCode (e .Get (`target` ).Get (`value` ).String ())
34- }, []any {})
35-
36- onKeyDown := UseCallback (func (e * js.Object ) {
37- key := e .Get (`key` ).String ()
38- shift := e .Get (`shiftKey` ).Bool ()
39- ctrl := e .Get (`metaKey` ).Bool () || e .Get (`ctrlKey` ).Bool ()
40- cba := & codeBoxAssistant {
32+ newCBA := func () * codeBoxAssistant {
33+ return & codeBoxAssistant {
4134 curCode : curCode ,
4235 setCode : setCode ,
4336 onSave : onSave ,
4437 onEscape : onEscape ,
4538 textAreaRef : textAreaRef ,
4639 }
47- if editor .ProcessKeyDown (cba , key , shift , ctrl ) {
40+ }
41+
42+ onInput := UseCallback (func (e * js.Object ) {
43+ code := e .Get (`target` ).Get (`value` ).String ()
44+ sel := getSelection (textAreaRef )
45+ globals .UndoRedo ().RecordCodeChange (sel , curCode , code )
46+ setCode (code )
47+ }, []any {curCode })
48+
49+ onKeyDown := UseCallback (func (e * js.Object ) {
50+ key := e .Get (`key` ).String ()
51+ shift := e .Get (`shiftKey` ).Bool ()
52+ ctrl := e .Get (`metaKey` ).Bool () || e .Get (`ctrlKey` ).Bool ()
53+ if editor .ProcessKeyDown (newCBA (), key , shift , ctrl ) {
4854 e .Call (`preventDefault` )
4955 e .Call (`stopPropagation` )
5056 }
@@ -55,14 +61,19 @@ func codeBoxComponent(props Props) *Element {
5561 lineNumsRef .Set (`scrollTop` , scrollTop )
5662 }, []any {})
5763
64+ onSelect := UseCallback (func (e * js.Object ) {
65+ // Don't normalize the selection so that the direction is preserved.
66+ globals .UndoRedo ().RecordSelectionChange (getSelection (textAreaRef ))
67+ }, []any {})
68+
5869 // TODO(grantnelson-wf): If it is possible to detect a paste event,
59- // then we could indent the pasted code automatically.
70+ // then maybe we could indent the pasted code automatically to match
71+ // the indent of where it is being pasted.
6072
6173 UseEffect (func () {
6274 // On first render, focus the code textarea.
6375 textAreaRef .Call (`focus` )
64- textAreaRef .Set (`selectionStart` , 0 )
65- textAreaRef .Set (`selectionEnd` , 0 )
76+ setSelection (textAreaRef , common.Selection {})
6677 }, []any {})
6778
6879 lineCount := strings .Count (curCode , "\n " ) + 1
@@ -87,6 +98,7 @@ func codeBoxComponent(props Props) *Element {
8798 `onInput` : onInput ,
8899 `onKeyDown` : onKeyDown ,
89100 `onScroll` : onScroll ,
101+ `onSelect` : onSelect ,
90102 `autoFocus` : true ,
91103 `autoCorrect` : `off` ,
92104 `autoComplete` : `off` ,
@@ -124,16 +136,19 @@ func (cba *codeBoxAssistant) EmitEvent(event common.Event) {
124136}
125137
126138func (cba * codeBoxAssistant ) GetSelection () common.Selection {
127- start := cba .textAreaRef .Get (`selectionStart` ).Int ()
128- end := cba .textAreaRef .Get (`selectionEnd` ).Int ()
129- if start > end {
130- // Reverse selection so start is always <= end.
131- return common.Selection {Start : end , End : start }
132- }
133- return common.Selection {Start : start , End : end }
139+ return getSelection (cba .textAreaRef ).Normalize ()
134140}
135141
136142func (cba * codeBoxAssistant ) SetCode (sel common.Selection , code string ) {
143+ // Record the code change for undo/redo.
144+ // Since this is used mostly for when the editor is doing some kind of edit
145+ // (otherwise the key down would be use the default behavior), we can
146+ // assume the code change should not be joined with other changes.
147+ // Use AddBreak to issolate this change.
148+ globals .UndoRedo ().AddBreak ()
149+ globals .UndoRedo ().RecordCodeChange (sel , cba .curCode , code )
150+ globals .UndoRedo ().AddBreak ()
151+
137152 // Update the code state for react.
138153 cba .setCode (code )
139154
@@ -142,15 +157,12 @@ func (cba *codeBoxAssistant) SetCode(sel common.Selection, code string) {
142157 cba .textAreaRef .Set (`value` , code )
143158
144159 // Match the diretionallity of the prior selection.
145- oldStart := cba .textAreaRef .Get (`selectionStart` ).Int ()
146- oldEnd := cba .textAreaRef .Get (`selectionEnd` ).Int ()
147- if oldStart > oldEnd {
160+ if getSelection (cba .textAreaRef ).Reversed () {
148161 sel .Start , sel .End = sel .End , sel .Start
149162 }
150163
151164 // Set selections
152- cba .textAreaRef .Set (`selectionStart` , sel .Start )
153- cba .textAreaRef .Set (`selectionEnd` , sel .End )
165+ setSelection (cba .textAreaRef , sel )
154166
155167 // Auto-scroll to keep caret in view.
156168 verticallyAutoScroll (cba .textAreaRef , sel .End , code )
@@ -196,6 +208,18 @@ func horizontallyAutoScroll(textAreaRef *Ref, caret int, code string) {
196208 }
197209}
198210
211+ func getSelection (textAreaRef * Ref ) common.Selection {
212+ start := textAreaRef .Get (`selectionStart` ).Int ()
213+ end := textAreaRef .Get (`selectionEnd` ).Int ()
214+ return common.Selection {Start : start , End : end }
215+ }
216+
217+ // setSelection sets the selection on the given textarea ref.
218+ func setSelection (textAreaRef * Ref , sel common.Selection ) {
219+ textAreaRef .Set (`selectionStart` , sel .Start )
220+ textAreaRef .Set (`selectionEnd` , sel .End )
221+ }
222+
199223func getLineNumbers (lineCount int ) string {
200224 lines := make ([]string , lineCount )
201225 for i := 0 ; i < lineCount ; i ++ {
0 commit comments