Skip to content

Commit

Permalink
Fix [count]: initial range text in ex field
Browse files Browse the repository at this point in the history
Fixes regression from changes in ex field handling
  • Loading branch information
citizenmatt committed May 30, 2024
1 parent 1987a15 commit 26d1e81
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 21 deletions.
18 changes: 5 additions & 13 deletions src/main/java/com/maddyhome/idea/vim/group/ProcessGroup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,11 @@ public class ProcessGroup : VimProcessGroupBase() {
panel.deactivate(true, resetCaret)
}

private fun getRange(editor: VimEditor, cmd: Command): String {
var initText = ""
if (editor.inVisualMode) {
initText = "'<,'>"
} else if (cmd.rawCount > 0) {
initText = if (cmd.count == 1) {
"."
} else {
".,.+" + (cmd.count - 1)
}
}

return initText
private fun getRange(editor: VimEditor, cmd: Command) = when {
editor.inVisualMode -> "'<,'>"
cmd.rawCount == 1 -> "."
cmd.rawCount > 1 -> ".,.+" + (cmd.count - 1)
else -> ""
}

@Throws(ExecutionException::class, ProcessCanceledException::class)
Expand Down
35 changes: 33 additions & 2 deletions src/test/java/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,44 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

// TODO: Split this class
// This class should handle simple ex entry features, such as starting ex entry, accepting/cancelling, cursor shape etc.
// Individual actions such as c_CTRL-B or c_CTRL-E (beginning/end of line), c_CTRL-R (insert register), insert digraph
// or literal, etc. should have individual test classes in the ideavim.ex.action package
// :cmap should also be tested separately

class ExEntryTest : VimTestCase() {
@BeforeEach
override fun setUp(testInfo: TestInfo) {
super.setUp(testInfo)
configureByText("\n")
}

@Test
fun `test initial text set to empty string`() {
typeExInput(":")
assertExText("")
}

@Test
fun `test initial text set to current line range with count of 1`() {
typeExInput("1:")
assertExText(".")
}

@Test
fun `test initial text set to current line with offset for count greater than 1`() {
typeExInput("10:")
assertExText(".,.+9")
}

@Test
fun `test initial text set to visual marks when invoked in Visual mode`() {
configureByText("lorem ipsum\nlorem ipsum")
typeText("V", ":")
assertExText("'<,'>")
}

@Test
fun `test cancel entry`() {
assertFalse(options().incsearch)
Expand Down Expand Up @@ -642,8 +673,8 @@ class ExEntryTest : VimTestCase() {

private fun typeExInput(text: String) {
assertTrue(
text.startsWith(":") || text.startsWith('/') || text.startsWith('?'),
"Ex command must start with ':', '/' or '?'",
Regex("""\d*[:/?].*""").matches(text),
"Ex command must start with '[count]:', '[count]/' or '[count]?'",
)

val keys = mutableListOf<KeyStroke>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ import com.maddyhome.idea.vim.key.RootNode
import org.jetbrains.annotations.TestOnly
import javax.swing.KeyStroke

public class CommandBuilder(private var currentCommandPartNode: CommandPartNode<LazyVimCommand>): Cloneable {
public class CommandBuilder(
private var currentCommandPartNode: CommandPartNode<LazyVimCommand>,
initialUncommittedCount: Int = 0,
) : Cloneable {
private var commandParts = ArrayDeque<Command>()
private var keyList = mutableListOf<KeyStroke>()

public var commandState: CurrentCommandState = CurrentCommandState.NEW_COMMAND
public var count: Int = 0
public var count: Int = initialUncommittedCount
private set
public val keys: Iterable<KeyStroke> get() = keyList
public val register: Char?
Expand Down Expand Up @@ -230,11 +233,10 @@ public class CommandBuilder(private var currentCommandPartNode: CommandPartNode<
}

public override fun clone(): CommandBuilder {
val result = CommandBuilder(currentCommandPartNode)
val result = CommandBuilder(currentCommandPartNode, count)
result.commandParts = ArrayDeque(commandParts)
result.keyList = keyList.toMutableList()
result.commandState = commandState
result.count = count
result.expectedArgumentType = expectedArgumentType
result.prevExpectedArgumentType = prevExpectedArgumentType

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ public data class KeyHandlerState(
get() = commandLineCommandBuilder ?: editorCommandBuilder

public fun enterCommandLine() {
commandLineCommandBuilder = CommandBuilder(injector.keyGroup.getKeyRoot(MappingMode.CMD_LINE))
// Create a new command builder for the command line, so we can handle nested commands inside the command line.
// The command that starts the command line is added to the new command builder and immediately executed, opening
// the command line UI.
// When we match the command that accepts or cancels the command line, we remove this nested command builder, and
// that command is added to the editor command builder, immediately completed and executed.
// The user might already have entered some state in the editor command builder, specifically uncommitted count.
// E.g., the user might have typed `3:` expecting the command line to be displayed with `:.,.+2` as initial text.
// We do not reset the uncommitted count in the editor command builder. The Ex actions ignore it, preferring the
// range in the text command. The search actions use it, and it will be combined with an operator count as expected.
// E.g., `2d3/foo` will delete up to the 6th occurrence of `foo`
// TODO: 2d3/foo doesn't handle incsearch properly!
commandLineCommandBuilder = CommandBuilder(injector.keyGroup.getKeyRoot(MappingMode.CMD_LINE), editorCommandBuilder.count)
}

public fun leaveCommandLine() {
Expand Down Expand Up @@ -56,4 +67,4 @@ public data class KeyHandlerState(
commandLineCommandBuilder?.clone(),
)
}
}
}

0 comments on commit 26d1e81

Please sign in to comment.