mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
fix: Improve smart delete behavior
Signed-off-by: Myzel394 <50424412+Myzel394@users.noreply.github.com>
This commit is contained in:
parent
6d535a9adf
commit
7045f438b5
@ -27,35 +27,47 @@ data class SmartDeleteHandler(
|
|||||||
|
|
||||||
val position = selection.start
|
val position = selection.start
|
||||||
|
|
||||||
val rightBracketPosition =
|
when (position) {
|
||||||
findRightBracket(position);
|
0 -> return TextRange(0, 0)
|
||||||
val leftBracketPosition =
|
1 -> return TextRange(0, 1)
|
||||||
findLeftBracket(position)
|
}
|
||||||
?: return TextRange(
|
|
||||||
rightBracketPosition?.let { takeNextIfIsOperator(it) }
|
|
||||||
?.let { if (it > position) 0 else it } ?: 0,
|
|
||||||
position,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (rightBracketPosition == null) {
|
val bracketPos = findPreviousBracket(position.coerceAtMost(value.length - 1) - 1)
|
||||||
val rightBracketRelativeToLeftPosition = findRightBracket(leftBracketPosition + 1)
|
|
||||||
|
|
||||||
return if (rightBracketRelativeToLeftPosition == null) {
|
if (bracketPos == null) {
|
||||||
// 1+2(+5|+6
|
return TextRange(0, position)
|
||||||
TextRange((leftBracketPosition + 1).coerceAtMost(position), position)
|
}
|
||||||
|
|
||||||
|
val isAtLeftEdge =
|
||||||
|
position - 1 == bracketPos && value[bracketPos] == Token.Operator.leftBracket[0]
|
||||||
|
val isAtRightEdge =
|
||||||
|
position - 1 == bracketPos && value[bracketPos] == Token.Operator.rightBracket[0]
|
||||||
|
|
||||||
|
if (!isAtLeftEdge && !isAtRightEdge) {
|
||||||
|
return TextRange(bracketPos + 1, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAtRightEdge) {
|
||||||
|
val leftBracketPos = findClosingParenBackwards(bracketPos)
|
||||||
|
|
||||||
|
return if (leftBracketPos != null) {
|
||||||
|
TextRange(leftBracketPos + 1, position)
|
||||||
} else {
|
} else {
|
||||||
// 1+2(6)+5|+6
|
// Weird case, should not happen
|
||||||
TextRange(
|
TextRange(0, position + 1)
|
||||||
takeNextIfIsOperator(rightBracketRelativeToLeftPosition + 1).coerceAtMost(
|
|
||||||
position,
|
|
||||||
),
|
|
||||||
position,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val end = findClosingParen(leftBracketPosition)
|
val rightBracketPos = findClosingParen(bracketPos)
|
||||||
return TextRange((leftBracketPosition + 1).coerceAtMost(end), end);
|
|
||||||
|
if (rightBracketPos != null) {
|
||||||
|
return TextRange(bracketPos + 1, rightBracketPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find previous bracket and return range from there to cursor position
|
||||||
|
val previousBracketPos = findPreviousBracket(bracketPos - 1)?.let { it + 1 } ?: 0
|
||||||
|
|
||||||
|
return TextRange(previousBracketPos, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun takeNextIfIsOperator(position: Int): Int {
|
private fun takeNextIfIsOperator(position: Int): Int {
|
||||||
@ -68,6 +80,16 @@ data class SmartDeleteHandler(
|
|||||||
|
|
||||||
private fun isSelectionARange(): Boolean = selection.start != selection.end
|
private fun isSelectionARange(): Boolean = selection.start != selection.end
|
||||||
|
|
||||||
|
private fun findPreviousBracket(startPosition: Int): Int? {
|
||||||
|
for (index in startPosition.coerceAtMost(value.length - 1) downTo 0) {
|
||||||
|
if (value[index] == Token.Operator.rightBracket[0] || value[index] == Token.Operator.leftBracket[0]) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun findLeftBracket(startPosition: Int): Int? {
|
private fun findLeftBracket(startPosition: Int): Int? {
|
||||||
for (index in startPosition.coerceAtMost(value.length - 1) downTo 0) {
|
for (index in startPosition.coerceAtMost(value.length - 1) downTo 0) {
|
||||||
if (value[index] == Token.Operator.leftBracket[0]) {
|
if (value[index] == Token.Operator.leftBracket[0]) {
|
||||||
@ -79,7 +101,7 @@ data class SmartDeleteHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun findRightBracket(startPosition: Int): Int? {
|
private fun findRightBracket(startPosition: Int): Int? {
|
||||||
for (index in startPosition.coerceAtMost(value.length - 1) until value.length) {
|
for (index in startPosition.coerceAtMost(value.length - 1) downTo 0) {
|
||||||
if (value[index] == Token.Operator.rightBracket[0]) {
|
if (value[index] == Token.Operator.rightBracket[0]) {
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
@ -88,19 +110,60 @@ data class SmartDeleteHandler(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isAtEdge(position: Int): Boolean {
|
||||||
|
if (position == 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val previousCharacter = value[position.coerceAtMost(value.length - 1) - 1]
|
||||||
|
|
||||||
|
return previousCharacter == Token.Operator.leftBracket[0] || previousCharacter == Token.Operator.rightBracket[0]
|
||||||
|
}
|
||||||
|
|
||||||
// Based of https://stackoverflow.com/a/12752226/9878135
|
// Based of https://stackoverflow.com/a/12752226/9878135
|
||||||
fun findClosingParen(openPos: Int): Int {
|
fun findClosingParen(openPos: Int): Int? {
|
||||||
var closePos = openPos
|
var closePos = openPos
|
||||||
var counter = 1
|
var counter = 1
|
||||||
|
|
||||||
while (counter > 0) {
|
while (counter > 0) {
|
||||||
val c = value[++closePos]
|
val nextPos = ++closePos
|
||||||
|
|
||||||
|
if (nextPos >= value.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val c = value[nextPos]
|
||||||
if (c == Token.Operator.leftBracket[0]) {
|
if (c == Token.Operator.leftBracket[0]) {
|
||||||
counter++
|
counter++
|
||||||
} else if (c == Token.Operator.rightBracket[0]) {
|
} else if (c == Token.Operator.rightBracket[0]) {
|
||||||
counter--
|
counter--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (closePos == openPos) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return closePos
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findClosingParenBackwards(openPos: Int): Int? {
|
||||||
|
var closePos = openPos
|
||||||
|
var counter = 1
|
||||||
|
|
||||||
|
while (counter > 0) {
|
||||||
|
val c = value[--closePos]
|
||||||
|
if (c == Token.Operator.leftBracket[0]) {
|
||||||
|
counter--
|
||||||
|
} else if (c == Token.Operator.rightBracket[0]) {
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closePos == openPos) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return closePos
|
return closePos
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,7 @@ class SmartDeleteHandlerTest {
|
|||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2+3)",
|
"1+(2+3)",
|
||||||
TextRange(4, 4),
|
TextRange(4, 4),
|
||||||
TextRange(3, 6),
|
TextRange(3, 4),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class SmartDeleteHandlerTest {
|
|||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2+3)×54",
|
"1+(2+3)×54",
|
||||||
TextRange(9, 9),
|
TextRange(9, 9),
|
||||||
TextRange(8, 9),
|
TextRange(7, 9),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class SmartDeleteHandlerTest {
|
|||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2+(3+4))",
|
"1+(2+(3+4))",
|
||||||
TextRange(8, 8),
|
TextRange(8, 8),
|
||||||
TextRange(6, 9),
|
TextRange(6, 8),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +82,15 @@ class SmartDeleteHandlerTest {
|
|||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2+(3+4))",
|
"1+(2+(3+4))",
|
||||||
TextRange(4, 4),
|
TextRange(4, 4),
|
||||||
|
TextRange(3, 4),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test nested brackets, inside the outside one, at edge`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"1+(2+(3+4))",
|
||||||
|
TextRange(3, 3),
|
||||||
TextRange(3, 10),
|
TextRange(3, 10),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -89,18 +98,9 @@ class SmartDeleteHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `test nested empty brackets`() {
|
fun `test nested empty brackets`() {
|
||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2+())",
|
"1+(2+(6))",
|
||||||
TextRange(4, 4),
|
TextRange(3, 3),
|
||||||
TextRange(3, 7),
|
TextRange(3, 8),
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test pure empty nested brackets`() {
|
|
||||||
assertDeleteRange(
|
|
||||||
"1+((()))",
|
|
||||||
TextRange(5, 5),
|
|
||||||
TextRange(5, 5),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +122,24 @@ class SmartDeleteHandlerTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test right bracket cursor after it`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"5+(2+6)+4",
|
||||||
|
TextRange(8, 8),
|
||||||
|
TextRange(7, 8),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test right bracket cursor at edge`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"5+(2+6)+4",
|
||||||
|
TextRange(7, 7),
|
||||||
|
TextRange(3, 7),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test single bracket with cursor at 0,0`() {
|
fun `test single bracket with cursor at 0,0`() {
|
||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
@ -135,7 +153,7 @@ class SmartDeleteHandlerTest {
|
|||||||
fun `test nested brackets with operators`() {
|
fun `test nested brackets with operators`() {
|
||||||
assertDeleteRange(
|
assertDeleteRange(
|
||||||
"1+(2*(3+4))",
|
"1+(2*(3+4))",
|
||||||
TextRange(8, 8),
|
TextRange(6, 6),
|
||||||
TextRange(6, 9),
|
TextRange(6, 9),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -149,6 +167,42 @@ class SmartDeleteHandlerTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test empty brackets deletes rest`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"5+(",
|
||||||
|
TextRange(3, 3),
|
||||||
|
TextRange(0, 3),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `left bracket, no right bracket`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"1+(5+6+",
|
||||||
|
TextRange(8, 8),
|
||||||
|
TextRange(3, 8),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `no bracket`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"1+5+6",
|
||||||
|
TextRange(5, 5),
|
||||||
|
TextRange(0, 5),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `closed bracket before, but left bracket after`() {
|
||||||
|
assertDeleteRange(
|
||||||
|
"1+(5+6)+(5+",
|
||||||
|
TextRange(11, 11),
|
||||||
|
TextRange(9, 11),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun assertDeleteRange(input: String, selection: TextRange, expected: TextRange) {
|
private fun assertDeleteRange(input: String, selection: TextRange, expected: TextRange) {
|
||||||
val smartDeleteHandler =
|
val smartDeleteHandler =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user