Something went wrong. Try again.
A Thrift language server, formatter, and linter.
lsp linter helix vim vscode thrift
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126package sema
import ( cmpstd "cmp" "fmt" "slices")
// Apply returns content with the fixes applied. A fix is all-or-nothing:// when any of its edits overlaps an accepted fix's edits — or its own —// the whole fix is skipped. Insertions (empty spans) never conflict with// each other; at the same offset they land in argument order, and just// before a replacement starting there. applied and skipped partition fixes// in argument order. An edit outside content is an error: offsets come// from the same parse the fixes were computed on, so a out-of-range span// is a fixer bug, not a condition to skip over.func Apply(content []byte, fixes []Fix) (out []byte, applied, skipped []Fix, err error) { type edit struct { start, end int text string }
edits := make([][]edit, len(fixes)) accepted := make([]bool, len(fixes))
for i, fx := range fixes { es := make([]edit, 0, len(fx.Edits))
ok := true
for _, e := range fx.Edits { start, end := e.Span.Start.Offset, e.Span.End.Offset if start < 0 || start > end || end > len(content) { return nil, nil, nil, fmt.Errorf("fix %q: edit range [%d:%d) outside %d bytes of content", fx.Title, start, end, len(content)) }
es = append(es, edit{start, end, e.NewText}) }
slices.SortStableFunc(es, func(a, b edit) int { return cmpstd.Compare(a.start, b.start) })
for j := 1; j < len(es) && ok; j++ { if es[j-1].end > es[j].start { skipped = append(skipped, fx) ok = false } }
if !ok { continue }
edits[i] = es }
// Accept in argument order: the first fix asking for a region wins. taken := make([]edit, 0, len(fixes))
for i, fx := range fixes { if edits[i] == nil { continue }
if slices.ContainsFunc(taken, func(t edit) bool { return slices.ContainsFunc(edits[i], func(e edit) bool { return e.start < t.end && t.start < e.end }) }) { skipped = append(skipped, fx)
continue }
accepted[i] = true taken = append(taken, edits[i]...) }
// Ascending by start, zero-length first on ties, then splice back to // front: the reverse walk keeps earlier offsets valid, and the tie // order lands a same-offset insertion's text just before a // replacement starting there — both effects survive, whatever the // argument order. var flat []edit
for i, fx := range fixes { if accepted[i] { flat = append(flat, edits[i]...) applied = append(applied, fx) } }
slices.SortStableFunc(flat, func(a, b edit) int { if c := cmpstd.Compare(a.start, b.start); c != 0 { return c }
// Zero-length edits sort before a replacement starting at the // same offset; equal kinds keep argument order. aZero, bZero := a.start == a.end, b.start == b.end
switch { case aZero && !bZero: return -1 case bZero && !aZero: return 1 }
return 0 })
out = content
for i := len(flat) - 1; i >= 0; i-- { e := flat[i]
buf := make([]byte, 0, len(out)-(e.end-e.start)+len(e.text)) buf = append(buf, out[:e.start]...) buf = append(buf, e.text...) buf = append(buf, out[e.end:]...)
out = buf }
return out, applied, skipped, nil}