package diff import ( "bytes" "sort" "golang.org/x/tools/internal/span" ) func init() { ApplyEdits = applyEdits } func applyEdits(before string, edits []TextEdit) string { // Preconditions: // - all of the edits apply to before // - and all the spans for each TextEdit have the same URI // copy edits so we don't make a mess of the caller's slice s := make([]TextEdit, len(edits)) copy(s, edits) edits = s // TODO(matloob): Initialize the Converter Once? var conv span.Converter = span.NewContentConverter("", []byte(before)) offset := func(point span.Point) int { if point.HasOffset() { return point.Offset() } offset, err := conv.ToOffset(point.Line(), point.Column()) if err != nil { panic(err) } return offset } // sort the copy sort.Slice(edits, func(i, j int) bool { return offset(edits[i].Span.Start()) < offset(edits[j].Span.Start()) }) var after bytes.Buffer beforeOffset := 0 for _, edit := range edits { if offset(edit.Span.Start()) < beforeOffset { panic("overlapping edits") // TODO(matloob): ApplyEdits doesn't return an error. What do we do? } else if offset(edit.Span.Start()) > beforeOffset { after.WriteString(before[beforeOffset:offset(edit.Span.Start())]) beforeOffset = offset(edit.Span.Start()) } // offset(edit.Span.Start) is now equal to beforeOffset after.WriteString(edit.NewText) beforeOffset += offset(edit.Span.End()) - offset(edit.Span.Start()) } if beforeOffset < len(before) { after.WriteString(before[beforeOffset:]) beforeOffset = len(before[beforeOffset:]) // just to preserve invariants } return after.String() }