@@ -213,19 +213,38 @@ ${pushHooks}\t\ttoks = append(toks, Tok{kind, text, off, end, pendingNl}); pendi
213213\t_ = pushTok
214214`
215215 : `\tpushTok := func(kind, text string, off, end int) { toks = append(toks, Tok{kind, text, off, end, pendingNl}); pendingNl = false }\n\t_ = pushTok\n` ;
216- return `${ defs . length ? 'var _s string\n' + defs . join ( '\n' ) + '\n' : '' } func lex(src string) []Tok {
216+ const pushTokAccFn = `\tpushTok := func(kind, text string, off, end int) { *acc = append(*acc, Tok{kind, text, off, end, pendingNl}); pendingNl = false }
217+ \t_ = pushTok
218+ ` ;
219+ const loopBody = `${ nlBoundary } \t\tc := int(src[pos])
220+ ${ nlWs } ${ tplDispatch } ${ toks }
221+ ${ puncts }
222+ \t\tpanic(fmt.Sprintf("lex error at %d", pos))` ;
223+ if ( stateful ) {
224+ return `${ defs . length ? 'var _s string\n' + defs . join ( '\n' ) + '\n' : '' } func lex(src string) []Tok {
217225\ttoks := toks[:0]
218226\tn := len(src)
219227\tpos := 0
220228\tpendingNl := false
221229\t_ = pendingNl
222230${ rxState } ${ tplState } ${ nlState } ${ emitFn } ${ pushTokFn } ${ defs . length ? '\t_s = src\n' : '' } \tfor pos < n {
223- ${ nlBoundary } \t\tc := int(src[pos])
224- ${ nlWs } ${ tplDispatch } ${ toks }
225- ${ puncts }
226- \t\tpanic(fmt.Sprintf("lex error at %d", pos))
231+ ${ loopBody }
227232\t}
228233\treturn toks
234+ }` ;
235+ }
236+ return `${ defs . length ? 'var _s string\n' + defs . join ( '\n' ) + '\n' : '' } func lexFrom(src string, pos int, pendingNl bool, acc *[]Tok, limit int) (int, bool) {
237+ \tn := len(src)
238+ ${ pushTokAccFn } ${ defs . length ? '\t_s = src\n' : '' } \tbase := len(*acc)
239+ \tfor pos < n && (limit <= 0 || len(*acc)-base < limit) {
240+ ${ loopBody }
241+ \t}
242+ \treturn pos, pendingNl
243+ }
244+ func lex(src string) []Tok {
245+ \tvar out []Tok
246+ \tlexFrom(src, 0, false, &out, 0)
247+ \treturn out
229248}` ;
230249}
231250
@@ -373,6 +392,159 @@ ${r.nudSeqs.map((seq) => `\t{ save := pos; sb := len(scratch); nb := len(nodes);
373392}` ;
374393}
375394
395+ function docEditBlockGo ( ir : ParserIR ) : string {
396+ const stateless = ! ( ir . regexCtx || ir . tpl || ir . newlineCfg ) ;
397+ const windowHelpers = stateless ? `
398+ func findTokAtOff(toks []alignMeta, off int) int {
399+ \tlo, hi := 0, len(toks)-1
400+ \tfor lo <= hi {
401+ \t\tmid := (lo + hi) >> 1
402+ \t\tif toks[mid].Off < off { lo = mid + 1 } else if toks[mid].Off > off { hi = mid - 1 } else { return mid }
403+ \t}
404+ \treturn -1
405+ }
406+ func windowRelexStep(oldText string, oldToks []alignMeta, newText string, start, end int, ins string) ([]alignMeta, int) {
407+ \tdelta := len(ins) - (end - start)
408+ \teditEnd := start + len(ins)
409+ \tmaxIdx := -1
410+ \tfor i := 0; i < len(oldToks); i++ {
411+ \t\tif oldToks[i].End < start { maxIdx = i } else { break }
412+ \t}
413+ \trb := -1
414+ \tif maxIdx >= 0 { rb = maxIdx - 1 }
415+ \tvar out []alignMeta
416+ \tif rb >= 0 { out = append(out, oldToks[:rb+1]...) }
417+ \tscanOff := 0
418+ \tif rb >= 0 { scanOff = oldToks[rb].End }
419+ \tpendingNl := false
420+ \tvar scratch []Tok
421+ \trelexed := 0
422+ \tfor scanOff < len(newText) {
423+ \t\tbefore := len(scratch)
424+ \t\tscanOff, pendingNl = lexFrom(newText, scanOff, pendingNl, &scratch, 1)
425+ \t\tif len(scratch) == before { break }
426+ \t\tt := scratch[len(scratch)-1]
427+ \t\tout = append(out, alignMeta{t.Kind, t.Off, t.End, t.Nl})
428+ \t\trelexed++
429+ \t\tif t.Off >= editEnd {
430+ \t\t\toIdx := findTokAtOff(oldToks, t.Off-delta)
431+ \t\t\tif oIdx >= 0 {
432+ \t\t\t\to := oldToks[oIdx]
433+ \t\t\t\tif o.Kind == t.Kind && o.End == t.End-delta && o.Nl == t.Nl && oldText[o.Off:o.End] == newText[t.Off:t.End] {
434+ \t\t\t\t\tfor j := oIdx + 1; j < len(oldToks); j++ {
435+ \t\t\t\t\t\tot := oldToks[j]
436+ \t\t\t\t\t\tout = append(out, alignMeta{ot.Kind, ot.Off + delta, ot.End + delta, ot.Nl})
437+ \t\t\t\t\t}
438+ \t\t\t\t\treturn out, relexed
439+ \t\t\t\t}
440+ \t\t\t}
441+ \t\t}
442+ \t}
443+ \treturn out, relexed
444+ }
445+ ` : '' ;
446+ const editBody = stateless
447+ ? `\tcurText := d.text
448+ \tcurToks := d.toks
449+ \tfor _, e := range edits {
450+ \t\tstepOldText, stepOldToks := curText, curToks
451+ \t\tn := len(curText)
452+ \t\tstart, end := e.Start, e.End
453+ \t\tif start < 0 { start = 0 }
454+ \t\tif start > n { start = n }
455+ \t\tif end < start { end = start }
456+ \t\tif end > n { end = n }
457+ \t\tins := e.Text
458+ \t\tcurText = curText[:start] + ins + curText[end:]
459+ \t\tvar stepRelexed int
460+ \t\tcurToks, stepRelexed = windowRelexStep(stepOldText, stepOldToks, curText, start, end, ins)
461+ \t\trelexed += stepRelexed
462+ \t}
463+ \td.text = curText
464+ \td.toks = curToks`
465+ : `\tfor _, e := range edits { d.text = applyEdit(d.text, e) }
466+ \tnewToks := tokenize(d.text)
467+ \td.toks = toMeta(newToks)
468+ \trelexed = len(d.toks)` ;
469+ return `type Edit struct { Start, End int; Text string }
470+ type alignMeta struct { Kind string; Off, End int; Nl bool }
471+ type Align struct {
472+ \tOldN int \`json:"oldN"\`
473+ \tNewN int \`json:"newN"\`
474+ \tPrefix int \`json:"prefix"\`
475+ \tSuffix int \`json:"suffix"\`
476+ \tRelexed int \`json:"relexed"\`
477+ \tStreamEq bool \`json:"streamEq"\`
478+ }
479+ func toMeta(toks []Tok) []alignMeta {
480+ \tm := make([]alignMeta, len(toks))
481+ \tfor i, t := range toks { m[i] = alignMeta{t.Kind, t.Off, t.End, t.Nl} }
482+ \treturn m
483+ }
484+ func computeAlignCore(oldText string, oldToks []alignMeta, newText string, newToks []alignMeta) (oldN, newN, prefix, suffix int) {
485+ \toldN, newN = len(oldToks), len(newToks)
486+ \tfor prefix < oldN && prefix < newN {
487+ \t\to, n := oldToks[prefix], newToks[prefix]
488+ \t\tif o.Kind != n.Kind || o.Off != n.Off || o.End != n.End || o.Nl != n.Nl { break }
489+ \t\tif oldText[o.Off:o.End] != newText[n.Off:n.End] { break }
490+ \t\tprefix++
491+ \t}
492+ \tdelta := len(newText) - len(oldText)
493+ \tminN := oldN; if newN < minN { minN = newN }
494+ \tfor prefix+suffix < minN {
495+ \t\to, n := oldToks[oldN-1-suffix], newToks[newN-1-suffix]
496+ \t\tif o.Kind != n.Kind || o.Nl != n.Nl || n.Off != o.Off+delta || n.End != o.End+delta { break }
497+ \t\tif oldText[o.Off:o.End] != newText[n.Off:n.End] { break }
498+ \t\tsuffix++
499+ \t}
500+ \treturn
501+ }
502+ func toksFromMeta(text string, meta []alignMeta) []Tok {
503+ \tt := make([]Tok, len(meta))
504+ \tfor i, m := range meta { t[i] = Tok{m.Kind, text[m.Off:m.End], m.Off, m.End, m.Nl} }
505+ \treturn t
506+ }
507+ func checkStreamEq(text string, meta []alignMeta) bool {
508+ \tfresh := toMeta(tokenize(text))
509+ \tif len(fresh) != len(meta) { return false }
510+ \tfor i := range fresh {
511+ \t\tf, m := fresh[i], meta[i]
512+ \t\tif f.Kind != m.Kind || f.Off != m.Off || f.End != m.End || f.Nl != m.Nl { return false }
513+ \t\tif text[f.Off:f.End] != text[m.Off:m.End] { return false }
514+ \t}
515+ \treturn true
516+ }
517+ ${ windowHelpers } type Doc struct { text string; root int32; toks []alignMeta; align *Align }
518+ func NewDoc(src string) *Doc {
519+ \td := &Doc{text: src}
520+ \td.toks = toMeta(tokenize(src))
521+ \td.root = parse(tokenize(src))
522+ \treturn d
523+ }
524+ func (d *Doc) Text() string { return d.text }
525+ func (d *Doc) Root() int32 { return d.root }
526+ func (d *Doc) Align() *Align { return d.align }
527+ func applyEdit(text string, e Edit) string {
528+ \tn := len(text)
529+ \tstart, end := e.Start, e.End
530+ \tif start < 0 { start = 0 }
531+ \tif start > n { start = n }
532+ \tif end < start { end = start }
533+ \tif end > n { end = n }
534+ \treturn text[:start] + e.Text + text[end:]
535+ }
536+ func (d *Doc) Edit(edits []Edit) int32 {
537+ \toldText, oldToks := d.text, d.toks
538+ \trelexed := 0
539+ ${ editBody }
540+ \tstreamEq := checkStreamEq(d.text, d.toks)
541+ \toldN, newN, prefix, suffix := computeAlignCore(oldText, oldToks, d.text, d.toks)
542+ \td.align = &Align{oldN, newN, prefix, suffix, relexed, streamEq}
543+ \td.root = parse(toksFromMeta(d.text, d.toks))
544+ \treturn d.root
545+ }` ;
546+ }
547+
376548export const goTarget : Target = {
377549 name : 'go' ,
378550 ext : 'go' ,
@@ -551,68 +723,7 @@ func parse(t []Tok) int32 {
551723\treturn parse${ ir . entry } ()
552724}
553725
554- type Edit struct { Start, End int; Text string }
555- type alignMeta struct { Kind string; Off, End int; Nl bool }
556- type Align struct {
557- \tOldN int \`json:"oldN"\`
558- \tNewN int \`json:"newN"\`
559- \tPrefix int \`json:"prefix"\`
560- \tSuffix int \`json:"suffix"\`
561- }
562- func toMeta(toks []Tok) []alignMeta {
563- \tm := make([]alignMeta, len(toks))
564- \tfor i, t := range toks { m[i] = alignMeta{t.Kind, t.Off, t.End, t.Nl} }
565- \treturn m
566- }
567- func computeAlign(oldText string, oldToks []alignMeta, newText string, newToks []alignMeta) Align {
568- \toldN, newN := len(oldToks), len(newToks)
569- \tprefix := 0
570- \tfor prefix < oldN && prefix < newN {
571- \t\to, n := oldToks[prefix], newToks[prefix]
572- \t\tif o.Kind != n.Kind || o.Off != n.Off || o.End != n.End || o.Nl != n.Nl { break }
573- \t\tif oldText[o.Off:o.End] != newText[n.Off:n.End] { break }
574- \t\tprefix++
575- \t}
576- \tdelta := len(newText) - len(oldText)
577- \tminN := oldN; if newN < minN { minN = newN }
578- \tsuffix := 0
579- \tfor prefix+suffix < minN {
580- \t\to, n := oldToks[oldN-1-suffix], newToks[newN-1-suffix]
581- \t\tif o.Kind != n.Kind || o.Nl != n.Nl || n.Off != o.Off+delta || n.End != o.End+delta { break }
582- \t\tif oldText[o.Off:o.End] != newText[n.Off:n.End] { break }
583- \t\tsuffix++
584- \t}
585- \treturn Align{oldN, newN, prefix, suffix}
586- }
587- type Doc struct { text string; root int32; toks []alignMeta; align *Align }
588- func NewDoc(src string) *Doc {
589- \td := &Doc{text: src}
590- \td.toks = toMeta(tokenize(src))
591- \td.root = parse(tokenize(src))
592- \treturn d
593- }
594- func (d *Doc) Text() string { return d.text }
595- func (d *Doc) Root() int32 { return d.root }
596- func (d *Doc) Align() *Align { return d.align }
597- func applyEdit(text string, e Edit) string {
598- \tn := len(text)
599- \tstart, end := e.Start, e.End
600- \tif start < 0 { start = 0 }
601- \tif start > n { start = n }
602- \tif end < start { end = start }
603- \tif end > n { end = n }
604- \treturn text[:start] + e.Text + text[end:]
605- }
606- func (d *Doc) Edit(edits []Edit) int32 {
607- \toldText, oldToks := d.text, d.toks
608- \tfor _, e := range edits { d.text = applyEdit(d.text, e) }
609- \tnewToks := tokenize(d.text)
610- \td.toks = toMeta(newToks)
611- \ta := computeAlign(oldText, oldToks, d.text, d.toks)
612- \td.align = &a
613- \td.root = parse(newToks)
614- \treturn d.root
615- }
726+ ${ docEditBlockGo ( ir ) }
616727` ;
617728 } ,
618729 emitRunner ( ) : string {
0 commit comments