@@ -11,21 +11,32 @@ const tabWidth = 4
1111
1212// CodeBox creates a code editor box React element for editing
1313// the given code state.
14- func CodeBox (code string , setCode func (any )) * Element {
14+ func CodeBox (code string , setCode func (any ), onSave func (), onEscape func () ) * Element {
1515 return CreateElement (codeBoxComponent , Props {
16- `curCode` : code ,
17- `setCode` : setCode ,
16+ `curCode` : code ,
17+ `setCode` : setCode ,
18+ `onSave` : onSave ,
19+ `onEscape` : onEscape ,
1820 })
1921}
2022
2123func codeBoxComponent (props Props ) * Element {
2224 cba := & codeBoxAssistant {
2325 curCode : As [string ](props , `curCode` ),
2426 setCode : AsSetter (props , `setCode` ),
27+ onSave : AsFunc (props , `onSave` ),
28+ onEscape : AsFunc (props , `onEscape` ),
2529 textAreaRef : UseRef (),
2630 lineNumsRef : UseRef (),
2731 }
2832
33+ UseEffect (func () {
34+ // On first render, focus the code textarea.
35+ cba .textAreaRef .Call (`focus` )
36+ cba .textAreaRef .Set (`selectionStart` , 0 )
37+ cba .textAreaRef .Set (`selectionEnd` , 0 )
38+ }, []any {})
39+
2940 lineCount := strings .Count (cba .curCode , "\n " ) + 1
3041 lineNumbers := UseMemo (func () string {
3142 return getLineNumbers (lineCount )
@@ -60,6 +71,8 @@ func codeBoxComponent(props Props) *Element {
6071type codeBoxAssistant struct {
6172 curCode string
6273 setCode func (any )
74+ onSave Func
75+ onEscape Func
6376 textAreaRef * Ref
6477 lineNumsRef * Ref
6578}
@@ -69,8 +82,6 @@ func (cba *codeBoxAssistant) onInput(e *js.Object) {
6982}
7083
7184func (cba * codeBoxAssistant ) onKeyDown (e * js.Object ) {
72- // TODO(grantnelson-wf): Maybe we should handle Ctrl+S for saving the code, just run formatting, or just ignore it.
73- // TODO(grantnelson-wf): If possible, make escape focus out of the code box since tabs won't switch focus anymore.
7485 key := e .Get (`key` ).String ()
7586 shift := e .Get (`shiftKey` ).Bool ()
7687 ctrl := e .Get (`metaKey` ).Bool () || e .Get (`ctrlKey` ).Bool ()
@@ -91,6 +102,8 @@ func (cba *codeBoxAssistant) handleKeyDown(key string, shift, ctrl bool) bool {
91102 return cba .handleTab (shift , ctrl )
92103 case `Enter` :
93104 return cba .handleNewline (shift , ctrl )
105+ case "s" :
106+ return cba .handleSave (shift , ctrl )
94107 case "*" :
95108 return cba .handleMultilineComment (shift , ctrl )
96109 case "/" :
@@ -107,6 +120,9 @@ func (cba *codeBoxAssistant) handleKeyDown(key string, shift, ctrl bool) bool {
107120 return cba .insertPair (ctrl , `{` , `}` )
108121 case "[" :
109122 return cba .insertPair (ctrl , `[` , `]` )
123+ case "Escape" :
124+ cba .onEscape ()
125+ return false
110126 default :
111127 return false
112128 }
@@ -185,20 +201,26 @@ func (cba *codeBoxAssistant) handleNewline(shift, ctrl bool) bool {
185201 if end >= 0 && end < len (cba .curCode ) {
186202 switch cba .curCode [end ] {
187203 case '}' , ')' , ']' :
188- opening := cba .findMatchingOpeningBrace (end )
189- println ("Found matching opening brace at:" , opening ) // TODO(grantnelson-wf): Remove
190- if opening >= 0 {
204+ if opening := cba .findMatchingOpeningBrace (end ); opening >= 0 {
191205 after = "\n " + cba .indentAt (opening )
192206 }
193207 }
194208 }
195209
196- println ("before:" , strconv .Quote (before )) // TODO(grantnelson-wf): Remove
197- println ("after: " , strconv .Quote (after )) // TODO(grantnelson-wf): Remove
198210 cba .insertAtSelection (before , after , false )
199211 return true
200212}
201213
214+ func (cba * codeBoxAssistant ) handleSave (shift , ctrl bool ) bool {
215+ if ! ctrl || shift {
216+ // Allow default behavior for 's' without a Ctrl or Shift+Ctrl+S.
217+ return false
218+ }
219+
220+ cba .onSave ()
221+ return true
222+ }
223+
202224// handleCommentToggle handles toggling comments on the current line or selection.
203225func (cba * codeBoxAssistant ) handleCommentToggle (shift , ctrl bool ) bool {
204226 if ! ctrl || shift {
0 commit comments