Avoid IndexOutOfBounds when edits go out of range (#4065)

While doing regular node manipulation in editior, noticed a number of situations when

```
java.lang.IndexOutOfBoundsException: None
at java.base/java.lang.Character.offsetByCodePoints(Character.java:8699)
at java.base/java.lang.String.offsetByCodePoints(String.java:820)
at org.enso.text.buffer.CodePointView$Ops$.drop(CodePointView.scala:98)
at org.enso.text.buffer.CodePointView$Ops$.drop(CodePointView.scala:57)
at org.enso.text.buffer.Node.drop(Tree.scala:218)
at org.enso.text.buffer.Rope.dropWith(Rope.scala:86)
at org.enso.text.buffer.CodePointView.drop(CodePointView.scala:30)
at org.enso.text.editing.RopeTextEditor$.cutOutTail(RopeTextEditor.scala:42)
...
```
would be thrown. Further text edits would simply be rejected requiring a complete restart. I doubt we should propagate
`IndexOutOfBoundsException`. Instead it is safer to just apply the edit to the end of the rope.
This commit is contained in:
Hubert Plociniczak 2023-01-19 12:43:46 +01:00 committed by GitHub
parent fcc2163ae3
commit 246755d29b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 22 additions and 3 deletions

View File

@ -35,11 +35,13 @@ object RopeTextEditor extends TextEditor[Rope] {
fullLines ++ rest
}
private def cutOutTail(buffer: Rope, diff: TextEdit): Rope =
buffer.lines
private def cutOutTail(buffer: Rope, diff: TextEdit): Rope = {
val codePoints = buffer.lines
.drop(diff.range.end.line)
.codePoints
.drop(diff.range.end.character)
if (codePoints.length < diff.range.end.character) Rope.empty
else codePoints.drop(diff.range.end.character)
}
/** Returns a number of lines in a buffer.
*

View File

@ -40,6 +40,23 @@ class RopeTextEditorSpec extends AnyFlatSpec with Matchers {
| result""".stripMargin
}
it should "not crash in a replacement that is longer" in {
//given
val mainPosition =
Range(Position(6, 4), Position(6, 12)) // result ends at (6, 10)
val mainReplacement = TextEdit(mainPosition, "result + 1")
//when
val result = RopeTextEditor.edit(testSnippet, mainReplacement)
//then
result.toString mustBe """
|main =
| apply = v f -> f v
| adder = a b -> a + b
| plusOne = apply (f = adder 1)
| result = plusOne 10
| result + 1""".stripMargin
}
it should "replace a multiline substring" in {
//given
val resultPosition = Range(Position(5, 4), Position(6, 10))