11package react
22
33import (
4- "fmt "
4+ "strconv "
55 "strings"
66
77 "github.com/gopherjs/gopherjs/js"
88)
99
10+ const tabWidth = 4
11+
1012// CodeBox creates a code editor box React element for editing
1113// the given code state.
12- func CodeBox (code string , setCode func (string )) * Element {
14+ func CodeBox (code string , setCode func (any )) * Element {
1315 return CreateElement (codeBoxComponent , Props {
1416 `curCode` : code ,
1517 `setCode` : setCode ,
@@ -19,53 +21,46 @@ func CodeBox(code string, setCode func(string)) *Element {
1921func codeBoxComponent (props Props ) * Element {
2022 cba := & codeBoxAssistant {
2123 curCode : As [string ](props , `curCode` ),
22- setCode : AsFunc (props , `setCode` ),
24+ setCode : AsSetter (props , `setCode` ),
2325 textAreaRef : UseRef (),
2426 lineNumsRef : UseRef (),
2527 }
2628
2729 return Div (Props {
28- `className` : `box` ,
29- `id` : `content` ,
30+ `id` : `code-box` ,
3031 },
31- Div (Props {
32- `className` : `box yellow` ,
33- `id` : `input` ,
34- },
35- CreateElement (`textarea` , Props {
36- `id` : `line-nums` ,
37- `ref` : cba .lineNumsRef ,
38- `value` : cba .getLineNumbers (),
39- `readOnly` : true ,
40- `disable` : `true` ,
41- }),
42- CreateElement (`textarea` , Props {
43- `id` : `code` ,
44- `ref` : cba .textAreaRef ,
45- `value` : cba .curCode ,
46- `onInput` : cba .onInput ,
47- `onKeyDown` : cba .onKeyDown ,
48- `onScroll` : cba .onScroll ,
49- `autoFocus` : true ,
50- `autoCorrect` : `off` ,
51- `autoComplete` : `off` ,
52- `autoCapitalize` : `off` ,
53- `spellCheck` : false ,
54- }),
55- ),
32+ CreateElement (`textarea` , Props {
33+ `id` : `line-nums` ,
34+ `ref` : cba .lineNumsRef ,
35+ `value` : cba .getLineNumbers (),
36+ `readOnly` : true ,
37+ `disable` : `true` ,
38+ }),
39+ CreateElement (`textarea` , Props {
40+ `id` : `code` ,
41+ `ref` : cba .textAreaRef ,
42+ `value` : cba .curCode ,
43+ `onInput` : cba .onInput ,
44+ `onKeyDown` : cba .onKeyDown ,
45+ `onScroll` : cba .onScroll ,
46+ `autoFocus` : true ,
47+ `autoCorrect` : `off` ,
48+ `autoComplete` : `off` ,
49+ `autoCapitalize` : `off` ,
50+ `spellCheck` : false ,
51+ }),
5652 )
5753}
5854
5955type codeBoxAssistant struct {
6056 curCode string
61- setCode Func
57+ setCode func ( any )
6258 textAreaRef * Ref
6359 lineNumsRef * Ref
6460}
6561
6662func (cba * codeBoxAssistant ) onInput (e * js.Object ) {
67- code := e .Get (`target` ).Get (`value` ).String ()
68- cba .setCode (code )
63+ cba .setCode (e .Get (`target` ).Get (`value` ).String ())
6964}
7065
7166func (cba * codeBoxAssistant ) onKeyDown (e * js.Object ) {
@@ -117,6 +112,11 @@ func (cba *codeBoxAssistant) handleKeyDown(keyCode int) bool {
117112}
118113
119114func (cba * codeBoxAssistant ) currentIndent (start int , code string ) string {
115+ if start < 0 || start > len (code ) {
116+ return ``
117+ }
118+
119+ // get prior line's indent
120120 par := strings .LastIndex (code [:start ], "\n " ) + 1
121121 i := par
122122 for i < start {
@@ -126,15 +126,21 @@ func (cba *codeBoxAssistant) currentIndent(start int, code string) string {
126126 }
127127 i ++
128128 }
129- return code [par :i ]
129+ indent := code [par :i ]
130+
131+ // adjust indent based on prior line's last character
132+ switch code [start ] {
133+ case '{' , '(' , '[' :
134+ indent += "\t "
135+ }
136+ return indent
130137}
131138
132139func (cba * codeBoxAssistant ) getLineNumbers () string {
133140 lines := strings .Count (cba .curCode , "\n " ) + 1
134- size := len (fmt .Sprintf (`%d` , lines ))
135141 var sb strings.Builder
136142 for i := 1 ; i <= lines ; i ++ {
137- sb .WriteString (fmt . Sprintf ( `%*d` , size , i ))
143+ sb .WriteString (strconv . Itoa ( i ))
138144 if i < lines {
139145 sb .WriteString ("\n " )
140146 }
@@ -194,23 +200,22 @@ func (cba *codeBoxAssistant) horizontallyAutoScroll(caret int, code string) {
194200 scrollLeft := int (float64 (curLine ) * float64 (totalWidth ) / float64 (longestLine ))
195201
196202 curLeft := cba .textAreaRef .Get (`scrollLeft` ).Int ()
197-
198- println (fmt .Sprintf ("HScroll: total=%d visible=%d longest=%d curLine=%d scrollLeft=%d curLeft=%d" ,
199- totalWidth , visibleWidth , longestLine , curLine , scrollLeft , curLeft ))
200-
201203 if scrollLeft < curLeft {
202204 cba .textAreaRef .Set (`scrollLeft` , scrollLeft )
203205 } else if scrollLeft -= visibleWidth ; scrollLeft > curLeft {
204206 cba .textAreaRef .Set (`scrollLeft` , scrollLeft )
205207 }
206208}
207209
210+ // measureLineLength returns the length of the line.
211+ // This counts tabs as multiple spaces so that the horizontal scrolling works correctly.
208212func measureLineLength (line string ) int {
209- const tabWidth = 4
210213 tabAdjust := strings .Count (line , "\t " ) * (tabWidth - 1 )
211214 return len (line ) + tabAdjust
212215}
213216
217+ // longestLineLength returns the length of the longest line in the given code.
218+ // This counts tabs as multiple spaces so that the horizontal scrolling works correctly.
214219func longestLineLength (code string ) int {
215220 longest := 0
216221 for {
0 commit comments