1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-24 22:42:53 +01:00

Compare commits

...

34 Commits

Author SHA1 Message Date
9101ca1afc
Set plugin version to chylex-37 2024-07-09 05:56:31 +02:00
7e493e0188
Revert per-caret registers 2024-07-09 05:56:31 +02:00
78b5540636
Revert "Factor disposable objects on editor opening"
This reverts commit 1fa78935
2024-07-09 05:48:41 +02:00
97ba6ae997
Fix(VIM-3364): Exception with mapped Generate action 2024-07-09 05:48:41 +02:00
9ac1a14604
Apply scrolloff after executing native IDEA actions 2024-07-09 05:48:35 +02:00
3c20feba8d
Stay on same line after reindenting 2024-07-09 05:48:29 +02:00
d23475818a
Update search register when using f/t 2024-07-09 05:48:29 +02:00
7f5c9b56ef
Automatically add unambiguous imports after running a macro 2024-07-09 05:48:29 +02:00
c3f5cb4866
Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2024-07-09 05:48:29 +02:00
7a25fbfd31
Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2024-07-09 05:48:29 +02:00
444880b153
Add support for count for visual and line motion surround 2024-07-09 05:48:29 +02:00
0ae06e46da
Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2024-07-09 05:48:29 +02:00
98e085e636
Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2024-07-09 05:48:28 +02:00
67c60ae8cc
Respect count with <Action> mappings 2024-07-09 05:48:28 +02:00
ffd2433da3
Change matchit plugin to use HTML patterns in unrecognized files 2024-07-09 05:48:28 +02:00
cfe219250c
Reset insert mode when switching active editor 2024-07-09 05:48:28 +02:00
68bdae1afc
Remove update checker 2024-07-09 05:48:28 +02:00
77859e03e7
Set custom plugin version 2024-07-09 05:48:28 +02:00
cfd05be288
Revert "Migrate to IntelliJ Platform Gradle Plugin 2.0"
This reverts commit 4913b13a
2024-07-09 05:48:28 +02:00
filipp
c0419d6018 Fix project leak 2024-07-08 13:09:08 +03:00
filipp
ea98e50f65 Sometimes safety is a bad thing 2024-07-08 13:09:08 +03:00
filipp
168174e383 Add missing resetOpPending to KeyHandler.reset 2024-07-08 13:09:08 +03:00
filipp
53cd4e1b88 Remove KeyHandlerStateResetter
It's an oneliner that we can live without
2024-07-08 13:09:08 +03:00
filipp
27e3561bb8 Safer VimListenersNotifier 2024-07-08 13:09:08 +03:00
filipp
9bb9cb13e3 Fix possible bug in the ExOutputModel.show() method and add documentation 2024-07-08 13:09:08 +03:00
filipp
16455f7241 Remove the update() method from VimOutputPanel 2024-07-08 13:09:08 +03:00
filipp
82225aa519 Use new interface instead of the old one 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
5f2baefc6c Introduce new interface for VimOutputPanel to support the output panel in Fleet
Why is the old interface bad?
- it is not obvious. You cannot create a new panel or check if it is already created. Only "getOrCreate" it
- output panel is bound to editor while in Vim it is global
- we have the `isActive` field and the `clear()` method at the same time, because interface implies that you store the same instance of the panel and reactivate it for each output and I don't like it. We also can forget to call `clear()` after reusing panel
- we cannot "build" output before showing to make the panel more efficient. With multiple carets we can only cal `output(oldText + newText)` for each caret, and it is bad. (imagine we have global command with a lot of matches and for each time we will need to call the `output(oldText + newText)`)
- the `output()` method shows panel, activates it and updates it
- there are more things that I do not like, but the points above should be already enough
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
cedcf39723 Fix(VIM-3461): Focus regression 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
4925d9aada Remove ideaglobalmode option
Mode is global now
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
f3e6df32d0 Fix(VIM-3462): IdeaVim not responsive occasionally
This bug was caused by two reasons:
1. KeyHandler state is not longer per-editor and we can't reset it on editor creation
2. We do not need to do such things on editor creation. What actually matters is focusing the editor
2024-07-08 13:09:08 +03:00
Filipp Vakhitov
5aaa8752af Move to new API from deprecated one 2024-07-08 13:09:08 +03:00
Filipp Vakhitov
a1d214316c Make VimStateMachine global
It is global in Vim
2024-07-08 13:09:08 +03:00
filipp
8a1e3eb066 Move toggleInsertOverwrite() to VimEditor
If we execute it from VimStateMachine directly, then mode change listeners are not notified
2024-07-08 13:09:08 +03:00
131 changed files with 1128 additions and 980 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

View File

@ -23,7 +23,7 @@ jobs:
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle testIdeUi -Doctopus.handler=false > build/reports/idea.log & gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@ -63,7 +63,7 @@ jobs:
# export DISPLAY=:99.0 # export DISPLAY=:99.0
# Xvfb -ac :99 -screen 0 1920x1080x16 & # Xvfb -ac :99 -screen 0 1920x1080x16 &
# mkdir -p build/reports # mkdir -p build/reports
# gradle :testIdeUi #> build/reports/idea.log # gradle :runIdeForUiTests #> build/reports/idea.log
# - name: Wait for Idea started # - name: Wait for Idea started
# uses: jtalk/url-health-check-action@1.5 # uses: jtalk/url-health-check-action@1.5
# with: # with:

View File

@ -26,7 +26,7 @@ jobs:
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle :testIdeUi -PideaType=PC > build/reports/idea.log & gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:

View File

@ -23,7 +23,7 @@ jobs:
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle testIdeUi > build/reports/idea.log & gradle runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@ -63,7 +63,7 @@ jobs:
# export DISPLAY=:99.0 # export DISPLAY=:99.0
# Xvfb -ac :99 -screen 0 1920x1080x16 & # Xvfb -ac :99 -screen 0 1920x1080x16 &
# mkdir -p build/reports # mkdir -p build/reports
# gradle :testIdeUi #> build/reports/idea.log # gradle :runIdeForUiTests #> build/reports/idea.log
# - name: Wait for Idea started # - name: Wait for Idea started
# uses: jtalk/url-health-check-action@1.5 # uses: jtalk/url-health-check-action@1.5
# with: # with:

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
*.swp *.swp
/.gradle/ /.gradle/
/.intellijPlatform/
/.idea/ /.idea/
!/.idea/scopes !/.idea/scopes

View File

@ -12,7 +12,7 @@
<option name="taskNames"> <option name="taskNames">
<list> <list>
<option value="check" /> <option value="check" />
<option value="verifyPlugin" /> <option value="runPluginVerifier" />
</list> </list>
</option> </option>
<option name="vmOptions" value="" /> <option name="vmOptions" value="" />
@ -20,7 +20,6 @@
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess> <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled> <DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" /> <method v="2" />
</configuration> </configuration>
</component> </component>

View File

@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
steps { steps {
gradle { gradle {
tasks = "clean verifyPlugin" tasks = "clean runPluginVerifier"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
} }

View File

@ -32,8 +32,6 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode import org.intellij.markdown.ast.getTextInNode
import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
import org.kohsuke.github.GHUser import org.kohsuke.github.GHUser
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
@ -69,20 +67,22 @@ plugins {
kotlin("jvm") version "1.9.22" kotlin("jvm") version "1.9.22"
application application
id("java-test-fixtures") id("java-test-fixtures")
id("org.jetbrains.intellij.platform") version "2.0.0-beta8"
id("org.jetbrains.intellij") version "1.17.3"
id("org.jetbrains.changelog") version "2.2.0" id("org.jetbrains.changelog") version "2.2.0"
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.dorongold.task-tree") version "4.0.0" id("com.dorongold.task-tree") version "4.0.0"
id("com.google.devtools.ksp") version "1.9.22-1.0.17" id("com.google.devtools.ksp") version "1.9.22-1.0.17"
} }
val moduleSources by configurations.registering
// Import variables from gradle.properties file // Import variables from gradle.properties file
val javaVersion: String by project val javaVersion: String by project
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaVersion: String by project val ideaVersion: String by project
val ideaType: String by project val ideaType: String by project
val downloadIdeaSources: String by project
val instrumentPluginCode: String by project val instrumentPluginCode: String by project
val remoteRobotVersion: String by project val remoteRobotVersion: String by project
val splitModeVersion: String by project val splitModeVersion: String by project
@ -97,9 +97,7 @@ val releaseType: String? by project
repositories { repositories {
mavenCentral() mavenCentral()
intellijPlatform { maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
defaultRepositories()
}
} }
dependencies { dependencies {
@ -110,27 +108,10 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
compileOnly("org.jetbrains:annotations:24.1.0") compileOnly("org.jetbrains:annotations:24.1.0")
intellijPlatform {
// Note that it is also possible to use local("...") to compile against a locally installed IDE
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
// Or something like: intellijIdeaUltimate(ideaVersion)
create(ideaType, ideaVersion)
pluginVerifier()
zipSigner()
instrumentationTools()
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.11")
}
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
// --------- Test dependencies ---------- // --------- Test dependencies ----------
testImplementation(testFixtures(project(":")))
testApi("com.squareup.okhttp3:okhttp:4.12.0") testApi("com.squareup.okhttp3:okhttp:4.12.0")
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
@ -204,37 +185,15 @@ tasks {
} }
} }
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
// a custom task (see below)
runIde { runIde {
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true) systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
} }
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies downloadRobotServerPlugin {
// Note that the version must be greater than the plugin's target version, for obvious reasons version.set(remoteRobotVersion)
// val runIdeCustom by intellijPlatformTesting.runIde.registering {
// type = IntelliJPlatformType.Rider
// version = "2024.1.2"
// }
// Uncomment to run the plugin in a locally installed IDE
// val runIdeLocal by intellijPlatformTesting.runIde.registering {
// localPath = file("/Users/{user}/Applications/WebStorm.app")
// }
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
splitMode = true
splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
// Frontend split mode support requires 242+
// TODO: Remove this once IdeaVim targets 242, as the task will naturally use the target version to run
version.set(splitModeVersion)
} }
// Start the default IDE with both IdeaVim and the robot server plugin installed, ready to run a UI test task. The runIdeForUiTests {
// robot server plugin is automatically added as a dependency to this task, and Gradle will take care of downloading.
// Note that the CustomTestIdeUiTask can be used to run tests against a different IDE
testIdeUi {
systemProperty("robot-server.port", "8082") systemProperty("robot-server.port", "8082")
systemProperty("ide.mac.message.dialogs.as.sheets", "false") systemProperty("ide.mac.message.dialogs.as.sheets", "false")
systemProperty("jb.privacy.policy.text", "<!--999.999-->") systemProperty("jb.privacy.policy.text", "<!--999.999-->")
@ -245,21 +204,28 @@ tasks {
} }
// Add plugin open API sources to the plugin ZIP // Add plugin open API sources to the plugin ZIP
val sourcesJar by registering(Jar::class) { val createOpenApiSourceJar by registering(Jar::class) {
dependsOn(moduleSources) // Java sources
destinationDirectory.set(layout.buildDirectory.dir("libs")) from(sourceSets.main.get().java) {
archiveClassifier.set(DocsType.SOURCES) include("**/com/maddyhome/idea/vim/**/*.java")
from(sourceSets.main.map { it.kotlin })
from(provider {
moduleSources.map {
it.map { jarFile -> zipTree(jarFile) }
} }
}) from(project(":vim-engine").sourceSets.main.get().java) {
include("**/com/maddyhome/idea/vim/**/*.java")
}
// Kotlin sources
from(kotlin.sourceSets.main.get().kotlin) {
include("**/com/maddyhome/idea/vim/**/*.kt")
}
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
include("**/com/maddyhome/idea/vim/**/*.kt")
}
destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveClassifier.set("src")
} }
buildPlugin { buildPlugin {
dependsOn(sourcesJar) dependsOn(createOpenApiSourceJar)
from(sourcesJar) { into("lib/src") } from(createOpenApiSourceJar) { into("lib/src") }
} }
} }
@ -284,44 +250,44 @@ gradle.projectsEvaluated {
// --- Intellij plugin // --- Intellij plugin
intellijPlatform { intellij {
pluginConfiguration { version.set(ideaVersion)
name = "IdeaVim" type.set(ideaType)
changeNotes.set( pluginName.set("IdeaVim")
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
)
ideaVersion { updateSinceUntilBuild.set(false)
// Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against
// specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified. downloadSources.set(downloadIdeaSources.toBoolean())
// The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it instrumentCode.set(instrumentPluginCode.toBoolean())
// with a null provider (the provider is important). intellijRepository.set("https://www.jetbrains.com/intellij-repository")
// By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of plugins.set(listOf("AceJump:3.8.11"))
// IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile
// against, we can never get an API that's newer - it would be an unresolved symbol.
untilBuild.set(provider { null })
}
} }
publishing { tasks {
publishPlugin {
channels.set(publishChannels.split(",")) channels.set(publishChannels.split(","))
token.set(publishToken) token.set(publishToken)
} }
signing { signPlugin {
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN")) certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
privateKey.set(providers.environmentVariable("PRIVATE_KEY")) privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
} }
verifyPlugin { runPluginVerifier {
teamCityOutputFormat = true downloadDir.set("${project.buildDir}/pluginVerifier/ides")
ides { teamCityOutputFormat.set(true)
recommended()
}
} }
instrumentCode.set(instrumentPluginCode.toBoolean()) patchPluginXml {
// Don't forget to update plugin.xml
sinceBuild.set("241.15989.150")
changeNotes.set(
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
)
}
} }
ksp { ksp {
@ -925,12 +891,12 @@ fun changes(): List<Change> {
println("Start changes processing") println("Start changes processing")
for (message in messages) { for (message in messages) {
println("Processing '$message'...") println("Processing '$message'...")
val lowercaseMessage = message.lowercase() val lowercaseMessage = message.toLowerCase()
val regex = "^fix\\((vim-\\d+)\\):".toRegex() val regex = "^fix\\((vim-\\d+)\\):".toRegex()
val findResult = regex.find(lowercaseMessage) val findResult = regex.find(lowercaseMessage)
if (findResult != null) { if (findResult != null) {
println("Message matches") println("Message matches")
val value = findResult.groups[1]!!.value.uppercase() val value = findResult.groups[1]!!.value.toUpperCase()
val shortMessage = message.drop(findResult.range.last + 1).trim() val shortMessage = message.drop(findResult.range.last + 1).trim()
newFixes += Change(value, shortMessage) newFixes += Change(value, shortMessage)
} else { } else {

View File

@ -19,8 +19,9 @@
ideaVersion=2024.1.1 ideaVersion=2024.1.1
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type # Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
ideaType=IC ideaType=IC
downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=SNAPSHOT version=chylex-37
javaVersion=17 javaVersion=17
remoteRobotVersion=0.11.22 remoteRobotVersion=0.11.22
antlrVersion=4.10.1 antlrVersion=4.10.1
@ -47,7 +48,6 @@ youtrackToken=
# Gradle settings # Gradle settings
org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.configuration-cache=true
org.gradle.caching=true org.gradle.caching=true
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary # Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary

View File

@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.MessageHelper import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
@ -102,7 +102,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val argument = cmd.argument ?: return false val argument = cmd.argument ?: return false
if (!editor.vimStateMachine.isDotRepeatInProgress) { if (!editor.inRepeatMode) {
argumentCaptured = argument argumentCaptured = argument
} }
val range = getMotionRange(editor, context, argument, operatorArguments) val range = getMotionRange(editor, context, argument, operatorArguments)

View File

@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
@ -25,7 +24,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean { override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
val state = editor.vimStateMachine val state = injector.vimState
val lastCommand = VimRepeater.lastChangeCommand val lastCommand = VimRepeater.lastChangeCommand
if (lastCommand == null && Extension.lastExtensionHandler == null) return false if (lastCommand == null && Extension.lastExtensionHandler == null) return false

View File

@ -9,51 +9,75 @@ package com.maddyhome.idea.vim.ex
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.api.VimExOutputPanel import com.maddyhome.idea.vim.api.VimOutputPanel
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.vimExOutput import com.maddyhome.idea.vim.helper.vimExOutput
import com.maddyhome.idea.vim.ui.ExOutputPanel import com.maddyhome.idea.vim.ui.ExOutputPanel
import java.lang.ref.WeakReference
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing // TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel { class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanel {
private var isActiveInTestMode = false private var isActiveInTestMode = false
override val isActive: Boolean val editor get() = myEditor.get()
val isActive: Boolean
get() = if (!ApplicationManager.getApplication().isUnitTestMode) { get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.isPanelActive(myEditor) editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
} else { } else {
isActiveInTestMode isActiveInTestMode
} }
override var text: String? = null override fun addText(text: String, isNewLine: Boolean) {
if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
}
override fun show() {
if (editor == null) return
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
if (currentPanel != null && currentPanel != this) currentPanel.close()
editor!!.vimExOutput = this
val exOutputPanel = ExOutputPanel.getInstance(editor!!)
if (!exOutputPanel.myActive) {
if (ApplicationManager.getApplication().isUnitTestMode) {
isActiveInTestMode = true
} else {
exOutputPanel.activate()
}
}
}
override var text: String = ""
get() = if (!ApplicationManager.getApplication().isUnitTestMode) { get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).text editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
} else { } else {
// ExOutputPanel always returns a non-null string // ExOutputPanel always returns a non-null string
field ?: "" field
} }
set(value) { set(value) {
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also // ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not // never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
val newValue = value?.removeSuffix("\n") val newValue = value.removeSuffix("\n")
if (!ApplicationManager.getApplication().isUnitTestMode) { if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).setText(newValue ?: "") editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
} else { } else {
field = newValue field = newValue
isActiveInTestMode = !newValue.isNullOrEmpty() isActiveInTestMode = newValue.isNotEmpty()
} }
} }
override fun output(text: String) { fun output(text: String) {
this.text = text this.text = text
} }
override fun clear() { fun clear() {
text = null text = ""
} }
override fun close() { override fun close() {
if (!ApplicationManager.getApplication().isUnitTestMode) { if (!ApplicationManager.getApplication().isUnitTestMode) {
ExOutputPanel.getInstance(myEditor).close() editor?.let { ExOutputPanel.getInstance(it).close() }
} }
else { else {
isActiveInTestMode = false isActiveInTestMode = false
@ -65,7 +89,7 @@ class ExOutputModel private constructor(private val myEditor: Editor) : VimExOut
fun getInstance(editor: Editor): ExOutputModel { fun getInstance(editor: Editor): ExOutputModel {
var model = editor.vimExOutput var model = editor.vimExOutput
if (model == null) { if (model == null) {
model = ExOutputModel(editor) model = ExOutputModel(WeakReference(editor))
editor.vimExOutput = model editor.vimExOutput = model
} }
return model return model

View File

@ -24,8 +24,8 @@ import com.maddyhome.idea.vim.common.CommandAlias
import com.maddyhome.idea.vim.common.CommandAliasHandler import com.maddyhome.idea.vim.common.CommandAliasHandler
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.TestInputModel import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.noneOfEnum import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
@ -68,7 +68,8 @@ object VimExtensionFacade {
@JvmStatic @JvmStatic
@Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", @Deprecated(
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
ReplaceWith( ReplaceWith(
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", "VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
"com.maddyhome.idea.vim.VimPlugin" "com.maddyhome.idea.vim.VimPlugin"
@ -150,7 +151,7 @@ object VimExtensionFacade {
/** Returns a single key stroke from the user input similar to 'getchar()'. */ /** Returns a single key stroke from the user input similar to 'getchar()'. */
@JvmStatic @JvmStatic
fun inputKeyStroke(editor: Editor): KeyStroke { fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.vim.vimStateMachine.isDotRepeatInProgress) { if (editor.vim.inRepeatMode) {
val input = Extension.consumeKeystroke() val input = Extension.consumeKeystroke()
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input") LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}") return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
@ -194,7 +195,7 @@ object VimExtensionFacade {
@JvmStatic @JvmStatic
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? { fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = caret.registerStorage.getRegister(register) ?: return null val reg = injector.registerGroup.getRegister(register) ?: return null
return reg.keys return reg.keys
} }
@ -207,7 +208,7 @@ object VimExtensionFacade {
/** Set the current contents of the given register */ /** Set the current contents of the given register */
@JvmStatic @JvmStatic
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) { fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList()) injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
} }
/** Set the current contents of the given register */ /** Set the current contents of the given register */

View File

@ -46,7 +46,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.exportOperatorFunction import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.handler.TextObjectActionHandler import com.maddyhome.idea.vim.handler.TextObjectActionHandler
import com.maddyhome.idea.vim.helper.PsiHelper import com.maddyhome.idea.vim.helper.PsiHelper
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
@ -65,7 +64,7 @@ internal class CommentaryExtension : VimExtension {
selectionType: SelectionType, selectionType: SelectionType,
resetCaret: Boolean = true, resetCaret: Boolean = true,
): Boolean { ): Boolean {
val mode = editor.vimStateMachine.mode val mode = editor.mode
if (mode !is Mode.VISUAL) { if (mode !is Mode.VISUAL) {
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset) editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
} }

View File

@ -234,7 +234,7 @@ private object FileTypePatterns {
} else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") { } else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
this.cMakePatterns this.cMakePatterns
} else { } else {
return null this.htmlPatterns
} }
} }

View File

@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension {
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) { private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
val registerGroup = injector.registerGroup val registerGroup = injector.registerGroup
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret() val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
var usedType = savedRegister.type var usedType = savedRegister.type
var usedText = savedRegister.text var usedText = savedRegister.text

View File

@ -0,0 +1,30 @@
package com.maddyhome.idea.vim.extension.surround
import com.intellij.util.text.CharSequenceSubSequence
internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence {
override val length = text.length * count
override fun get(index: Int): Char {
if (index < 0 || index >= length) throw IndexOutOfBoundsException()
return text[index % text.length]
}
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
return CharSequenceSubSequence(this, startIndex, endIndex)
}
override fun toString(): String {
return text.repeat(count)
}
companion object {
fun of(text: CharSequence, count: Int): CharSequence {
return when (count) {
0 -> ""
1 -> text
else -> RepeatedCharSequence(text, count)
}
}
}
}

View File

@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimChangeGroup
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.endsWithNewLine import com.maddyhome.idea.vim.api.endsWithNewLine
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
@ -35,7 +36,10 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
import com.maddyhome.idea.vim.extension.exportOperatorFunction import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.group.findBlockRange import com.maddyhome.idea.vim.group.findBlockRange
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
@ -78,7 +82,7 @@ internal class VimSurroundExtension : VimExtension {
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true) putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
} }
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator()) VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
} }
private class YSurroundHandler : ExtensionHandler { private class YSurroundHandler : ExtensionHandler {
@ -106,7 +110,7 @@ internal class VimSurroundExtension : VimExtension {
val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset) val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
if (lastNonWhiteSpaceOffset != null) { if (lastNonWhiteSpaceOffset != null) {
val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1) val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
performSurround(pair, range, it) performSurround(pair, range, it, count = operatorArguments.count1)
} }
// it.moveToOffset(lineStartOffset) // it.moveToOffset(lineStartOffset)
} }
@ -126,15 +130,13 @@ internal class VimSurroundExtension : VimExtension {
private class VSurroundHandler : ExtensionHandler { private class VSurroundHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
// NB: Operator ignores SelectionType anyway // NB: Operator ignores SelectionType anyway
if (!Operator().apply(editor, context, editor.mode.selectionType)) { if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) {
return return
} }
runWriteAction { runWriteAction {
// Leave visual mode // Leave visual mode
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
editor.ij.caretModel.moveToOffset(selectionStart)
} }
} }
} }
@ -155,6 +157,10 @@ internal class VimSurroundExtension : VimExtension {
companion object { companion object {
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) { fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
}
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
// Save old register values for carets // Save old register values for carets
val surroundings = editor.sortedCarets() val surroundings = editor.sortedCarets()
.map { .map {
@ -262,21 +268,42 @@ internal class VimSurroundExtension : VimExtension {
} }
} }
private class Operator : OperatorFunction { private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean { override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
val ijEditor = editor.ij val ijEditor = vimEditor.ij
val c = getChar(ijEditor) val c = getChar(ijEditor)
if (c.code == 0) return true if (c.code == 0) return true
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
// XXX: Will it work with line-wise or block-wise selections?
val range = getSurroundRange(editor.currentCaret()) ?: return false runWriteAction {
performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE) val change = VimPlugin.getChange()
if (supportsMultipleCursors) {
ijEditor.runWithEveryCaretAndRestore {
applyOnce(ijEditor, change, pair, count)
}
}
else {
applyOnce(ijEditor, change, pair, count)
// Jump back to start // Jump back to start
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor) executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
}
}
return true return true
} }
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) {
// XXX: Will it work with line-wise or block-wise selections?
val primaryCaret = editor.caretModel.primaryCaret
val range = getSurroundRange(primaryCaret.vim)
if (range != null) {
val start = RepeatedCharSequence.of(pair.first, count)
val end = RepeatedCharSequence.of(pair.second, count)
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start)
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end)
}
}
private fun getSurroundRange(caret: VimCaret): TextRange? { private fun getSurroundRange(caret: VimCaret): TextRange? {
val editor = caret.editor val editor = caret.editor
val ijEditor = editor.ij val ijEditor = editor.ij
@ -362,15 +389,15 @@ private fun getChar(editor: Editor): Char {
return res return res
} }
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) { private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
runWriteAction { runWriteAction {
val editor = caret.editor val editor = caret.editor
val change = VimPlugin.getChange() val change = VimPlugin.getChange()
val leftSurround = pair.first + if (tagsOnNewLines) "\n" else "" val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count)
val isEOF = range.endOffset == editor.text().length val isEOF = range.endOffset == editor.text().length
val hasNewLine = editor.endsWithNewLine() val hasNewLine = editor.endsWithNewLine()
val rightSurround = if (tagsOnNewLines) { val rightSurround = (if (tagsOnNewLines) {
if (isEOF && !hasNewLine) { if (isEOF && !hasNewLine) {
"\n" + pair.second "\n" + pair.second
} else { } else {
@ -378,7 +405,7 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret:
} }
} else { } else {
pair.second pair.second
} }).let { RepeatedCharSequence.of(it, count) }
change.insertText(editor, caret, range.startOffset, leftSurround) change.insertText(editor, caret, range.startOffset, leftSurround)
change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround) change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)

View File

@ -80,7 +80,6 @@ import java.math.BigInteger
import java.util.* import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
import kotlin.math.max import kotlin.math.max
import kotlin.math.min
/** /**
* Provides all the insert/replace related functionality * Provides all the insert/replace related functionality
@ -399,6 +398,7 @@ class ChangeGroup : VimChangeGroupBase() {
context: ExecutionContext, context: ExecutionContext,
range: TextRange, range: TextRange,
) { ) {
val startPos = editor.offsetToBufferPosition(caret.offset)
val startOffset = editor.getLineStartForOffset(range.startOffset) val startOffset = editor.getLineStartForOffset(range.startOffset)
val endOffset = editor.getLineEndForOffset(range.endOffset) val endOffset = editor.getLineEndForOffset(range.endOffset)
val ijEditor = (editor as IjVimEditor).editor val ijEditor = (editor as IjVimEditor).editor
@ -423,11 +423,7 @@ class ChangeGroup : VimChangeGroupBase() {
} }
} }
val afterAction = { val afterAction = {
val firstLine = editor.offsetToBufferPosition( caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line))
min(startOffset.toDouble(), endOffset.toDouble()).toInt()
).line
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
caret.moveToOffset(newOffset)
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line) restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
} }
if (project != null) { if (project != null) {

View File

@ -218,45 +218,6 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE); editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
} }
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
// editors. This includes editors used as part of the UI, such as the VCS commit message, or used as read-only
// viewers for text output, such as log files in run configurations or the Git Console tab. And editors are used for
// interactive stdin/stdout for console-based run configurations.
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
// to INSERT mode if they are interactive editors. Recognising these can be a bit tricky.
// These additional interactive editors are not file-based, but must have a writable document. However, log output
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
// read-only and also hides the caret).
// Furthermore, interactive stdin/stdout console output in run configurations is hosted in a read-only editor, but
// it can still be edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's
// `isViewer` property and allows typing if the associated process (if any) is still running. We can get the
// editor's console view and check this ourselves, but we have to wait until the editor has finished initialising
// before it's available in user data.
// Finally, we have a special check for diff windows. If we compare against clipboard, we get a diff editor that is
// not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
Runnable switchToInsertMode = () -> {
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
KeyHandler.getInstance().reset(new IjVimEditor(editor));
};
if (!editor.isViewer() &&
!EditorHelper.isFileEditor(editor) &&
editor.getDocument().isWritable() &&
!CommandStateHelper.inInsertMode(editor) &&
editor.getEditorKind() != EditorKind.DIFF) {
switchToInsertMode.run();
}
ApplicationManager.getApplication().invokeLater(
() -> {
if (editor.isDisposed()) return;
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
switchToInsertMode.run();
}
});
updateCaretsVisualAttributes(new IjVimEditor(editor)); updateCaretsVisualAttributes(new IjVimEditor(editor));
} }
@ -416,7 +377,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
// Note that IDE scale is handled by LafManager.lookAndFeelChanged // Note that IDE scale is handled by LafManager.lookAndFeelChanged
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine(); VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
if (activeCommandLine != null) { if (activeCommandLine != null) {
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), false); injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), true, false);
} }
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor); ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
if (exOutputModel != null) { if (exOutputModel != null) {

View File

@ -271,7 +271,7 @@ class FileGroup : VimFileBase() {
val msg = StringBuilder() val msg = StringBuilder()
val doc = editor.document val doc = editor.document
if (getInstance(IjVimEditor(editor)).mode !is VISUAL) { if (injector.vimState.mode !is VISUAL) {
val lp = editor.caretModel.logicalPosition val lp = editor.caretModel.logicalPosition
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line) val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
var endoff = doc.getLineEndOffset(lp.line) var endoff = doc.getLineEndOffset(lp.line)

View File

@ -0,0 +1,68 @@
package com.maddyhome.idea.vim.group
import com.intellij.codeInsight.daemon.ReferenceImporter
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import java.util.function.BooleanSupplier
internal object MacroAutoImport {
fun run(editor: Editor, dataContext: DataContext) {
val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) {
return
}
val importers = ReferenceImporter.EP_NAME.extensionList
if (importers.isEmpty()) {
return
}
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Auto import", true) {
override fun run(indicator: ProgressIndicator) {
val fixes = ReadAction.nonBlocking<List<BooleanSupplier>> {
val fixes = mutableListOf<BooleanSupplier>()
file.accept(object : PsiRecursiveElementWalkingVisitor() {
override fun visitElement(element: PsiElement) {
for (reference in element.references) {
if (reference.resolve() != null) {
continue
}
for (importer in importers) {
importer.computeAutoImportAtOffset(editor, file, element.textRange.startOffset, true)
?.let(fixes::add)
}
}
super.visitElement(element)
}
})
return@nonBlocking fixes
}.executeSynchronously()
ApplicationManager.getApplication().invokeAndWait {
WriteCommandAction.writeCommandAction(project)
.withName("Auto Import")
.withGroupId("IdeaVimAutoImportAfterMacro")
.shouldRecordActionForActiveDocument(true)
.run<RuntimeException> {
fixes.forEach { it.asBoolean }
}
}
}
})
}
}

View File

@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.MessageHelper.message import com.maddyhome.idea.vim.helper.MessageHelper.message
import com.maddyhome.idea.vim.macro.VimMacroBase import com.maddyhome.idea.vim.macro.VimMacroBase
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
/** /**
* Used to handle playback of macros * Used to handle playback of macros
@ -93,6 +94,9 @@ internal class MacroGroup : VimMacroBase() {
} finally { } finally {
keyStack.removeFirst() keyStack.removeFirst()
} }
if (!isInternalMacro) {
MacroAutoImport.run(editor.ij, context.ij)
}
} }
if (isInternalMacro) { if (isInternalMacro) {

View File

@ -47,7 +47,6 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
import com.maddyhome.idea.vim.helper.isEndAllowed import com.maddyhome.idea.vim.helper.isEndAllowed
import com.maddyhome.idea.vim.helper.vimLastColumn import com.maddyhome.idea.vim.helper.vimLastColumn
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.listener.AppCodeTemplates import com.maddyhome.idea.vim.listener.AppCodeTemplates
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
@ -307,13 +306,13 @@ internal class MotionGroup : VimMotionGroupBase() {
val editor = fileEditor.editor val editor = fileEditor.editor
if (!editor.isDisposed) { if (!editor.isDisposed) {
editor.vim.let { vimEditor -> editor.vim.let { vimEditor ->
when (vimEditor.vimStateMachine.mode) { when (vimEditor.mode) {
is Mode.VISUAL -> { is Mode.VISUAL -> {
vimEditor.exitVisualMode() vimEditor.exitVisualMode()
KeyHandler.getInstance().reset(vimEditor) KeyHandler.getInstance().reset(vimEditor)
} }
is Mode.CMD_LINE -> { is Mode.CMD_LINE -> {
injector.processGroup.cancelExEntry(vimEditor, false) injector.processGroup.cancelExEntry(vimEditor, refocusOwningEditor = false, resetCaret = false)
ExOutputModel.tryGetInstance(editor)?.close() ExOutputModel.tryGetInstance(editor)?.close()
} }
else -> {} else -> {}

View File

@ -73,7 +73,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
} }
private fun Editor.guicursorMode(): GuiCursorMode { private fun Editor.guicursorMode(): GuiCursorMode {
return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter) return GuiCursorMode.fromMode(vim.mode, injector.vimState.isReplaceCharacter)
} }
/** /**
@ -146,21 +146,15 @@ internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener { class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
override fun isReplaceCharChanged(editor: VimEditor) { override fun isReplaceCharChanged(editor: VimEditor) {
updateCaretsVisual(editor) updateCaretsVisual()
} }
override fun modeChanged(editor: VimEditor, oldMode: Mode) { override fun modeChanged(editor: VimEditor, oldMode: Mode) {
updateCaretsVisual(editor) updateCaretsVisual()
} }
private fun updateCaretsVisual(editor: VimEditor) { private fun updateCaretsVisual() {
if (injector.globalOptions().ideaglobalmode) {
updateAllEditorsCaretsVisual() updateAllEditorsCaretsVisual()
} else {
val ijEditor = (editor as IjVimEditor).editor
ijEditor.updateCaretsVisualAttributes()
ijEditor.updateCaretsVisualPosition()
}
} }
fun updateAllEditorsCaretsVisual() { fun updateAllEditorsCaretsVisual() {

View File

@ -34,4 +34,4 @@ val Editor.inVisualMode: Boolean
@get:JvmName("inExMode") @get:JvmName("inExMode")
internal val Editor.inExMode internal val Editor.inExMode
get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE get() = this.vim.mode is Mode.CMD_LINE

View File

@ -326,7 +326,7 @@ public class EditorHelper {
final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight); final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
@NotNull final VimEditor editor1 = new IjVimEditor(editor); @NotNull final VimEditor editor1 = new IjVimEditor(editor);
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1; final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount();
final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine); final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
// For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen. // For `zz`, we want to use virtual space and move any line, including the last one, to the middle of the screen.

View File

@ -12,6 +12,7 @@ package com.maddyhome.idea.vim.helper
import com.intellij.codeWithMe.ClientId import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.CaretState
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
@ -20,6 +21,8 @@ import com.maddyhome.idea.vim.api.StringListOptionValue
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.group.IjOptionConstants import com.maddyhome.idea.vim.group.IjOptionConstants
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.inBlockSelection
import java.awt.Component import java.awt.Component
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.JTable import javax.swing.JTable
@ -96,3 +99,41 @@ internal val Caret.vimLine: Int
*/ */
internal val Editor.vimLine: Int internal val Editor.vimLine: Int
get() = this.caretModel.currentCaret.vimLine get() = this.caretModel.currentCaret.vimLine
internal inline fun Editor.runWithEveryCaretAndRestore(action: () -> Unit) {
val caretModel = this.caretModel
val carets = if (this.vim.inBlockSelection) null else caretModel.allCarets
if (carets == null || carets.size == 1) {
action()
}
else {
var initialDocumentSize = this.document.textLength
var documentSizeDifference = 0
val caretOffsets = carets.map { it.selectionStart to it.selectionEnd }
val restoredCarets = mutableListOf<CaretState>()
caretModel.removeSecondaryCarets()
for ((selectionStart, selectionEnd) in caretOffsets) {
if (selectionStart == selectionEnd) {
caretModel.primaryCaret.moveToOffset(selectionStart + documentSizeDifference)
}
else {
caretModel.primaryCaret.setSelection(
selectionStart + documentSizeDifference,
selectionEnd + documentSizeDifference
)
}
action()
restoredCarets.add(caretModel.caretsAndSelections.single())
val documentLength = this.document.textLength
documentSizeDifference += documentLength - initialDocumentSize
initialDocumentSize = documentLength
}
caretModel.caretsAndSelections = restoredCarets
}
}

View File

@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.actionSystem.ex.ActionManagerEx import com.intellij.openapi.actionSystem.ex.ActionManagerEx
import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.actionSystem.impl.Utils
import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.UndoConfirmationPolicy import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
@ -86,6 +87,7 @@ internal class IjActionExecutor : VimActionExecutor {
ActionManager.getInstance(), ActionManager.getInstance(),
0, 0,
) )
Utils.initUpdateSession(event)
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems. // beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
// because rider use async update method. See VIM-1819. // because rider use async update method. See VIM-1819.
// This method executes inside of lastUpdateAndCheckDumb // This method executes inside of lastUpdateAndCheckDumb

View File

@ -31,7 +31,7 @@ import com.maddyhome.idea.vim.state.mode.returnTo
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) { internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.vim.inSelectMode) return if (!this.vim.inSelectMode) return
val returnTo = this.vim.vimStateMachine.mode.returnTo val returnTo = this.vim.mode.returnTo
when (returnTo) { when (returnTo) {
ReturnTo.INSERT -> { ReturnTo.INSERT -> {
this.vim.mode = Mode.INSERT this.vim.mode = Mode.INSERT
@ -64,7 +64,7 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) { internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
if (!this.inSelectMode) return if (!this.inSelectMode) return
val returnTo = this.vimStateMachine.mode.returnTo val returnTo = this.mode.returnTo
when (returnTo) { when (returnTo) {
ReturnTo.INSERT -> { ReturnTo.INSERT -> {
this.mode = Mode.INSERT this.mode = Mode.INSERT

View File

@ -60,7 +60,7 @@ internal object ScrollViewHelper {
// that this needs to be replaced as a more or less dumb line for line rewrite. // that this needs to be replaced as a more or less dumb line for line rewrite.
val topLine = getVisualLineAtTopOfScreen(editor) val topLine = getVisualLineAtTopOfScreen(editor)
val bottomLine = getVisualLineAtBottomOfScreen(editor) val bottomLine = getVisualLineAtBottomOfScreen(editor)
val lastLine = vimEditor.getVisualLineCount() - 1 val lastLine = vimEditor.getVisualLineCount() + editor.settings.additionalLinesCount
// We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
val scrollOffset = injector.options(vimEditor).scrolloff val scrollOffset = injector.options(vimEditor).scrolloff
@ -180,7 +180,7 @@ internal object ScrollViewHelper {
} }
private fun getScrollJump(editor: VimEditor, height: Int): Int { private fun getScrollJump(editor: VimEditor, height: Int): Int {
val flags = VimStateMachine.getInstance(editor).executingCommandFlags val flags = injector.vimState.executingCommandFlags
val scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP) val scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)
// Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line // Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line
@ -203,7 +203,7 @@ internal object ScrollViewHelper {
val caretColumn = position.column val caretColumn = position.column
val halfWidth = getApproximateScreenWidth(editor) / 2 val halfWidth = getApproximateScreenWidth(editor) / 2
val scrollOffset = getNormalizedSideScrollOffset(editor) val scrollOffset = getNormalizedSideScrollOffset(editor)
val flags = VimStateMachine.getInstance(vimEditor).executingCommandFlags val flags = injector.vimState.executingCommandFlags
val allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP) val allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP)
val sidescroll = injector.options(vimEditor).sidescroll val sidescroll = injector.options(vimEditor).sidescroll
val offsetLeft = caretColumn - (currentVisualLeftColumn + scrollOffset) val offsetLeft = caretColumn - (currentVisualLeftColumn + scrollOffset)

View File

@ -17,6 +17,7 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.fileEditor.TextEditor import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -26,6 +27,8 @@ import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.common.InsertSequence import com.maddyhome.idea.vim.common.InsertSequence
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.inVisualMode
import com.maddyhome.idea.vim.undo.UndoRedoBase import com.maddyhome.idea.vim.undo.UndoRedoBase
/** /**
@ -64,15 +67,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo // TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
editor.runWithChangeTracking { editor.runWithChangeTracking {
undoManager.undo(fileEditor) undoManager.undo(fileEditor)
restoreVisualMode(editor)
// We execute undo one more time if the previous one just restored selection
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
undoManager.undo(fileEditor)
}
}
CommandProcessor.getInstance().runUndoTransparentAction {
removeSelections(editor)
} }
} else { } else {
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) { runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
@ -219,4 +214,21 @@ internal class UndoRedoHelper : UndoRedoBase() {
val hasChanges: Boolean val hasChanges: Boolean
get() = changeListener.hasChanged || initialPath != editor.getPath() get() = changeListener.hasChanged || initialPath != editor.getPath()
} }
private fun restoreVisualMode(editor: VimEditor) {
if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) {
val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor)
// Visual block selection is restored into multiple carets, so multi-carets that form a block are always
// identified as visual block mode, leading to false positives.
// Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore
// visual block mode.
val wantedMode = if (detectedMode == SelectionType.BLOCK_WISE)
SelectionType.CHARACTER_WISE
else
detectedMode
VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
}
}
} }

View File

@ -18,13 +18,12 @@ import com.intellij.openapi.editor.VisualPosition
import com.intellij.openapi.editor.markup.RangeHighlighter import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder import com.intellij.openapi.util.UserDataHolder
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
import com.maddyhome.idea.vim.api.LocalMarkStorage import com.maddyhome.idea.vim.api.LocalMarkStorage
import com.maddyhome.idea.vim.api.SelectionInfo import com.maddyhome.idea.vim.api.SelectionInfo
import com.maddyhome.idea.vim.common.InsertSequence
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.group.visual.VisualChange import com.maddyhome.idea.vim.group.visual.VisualChange
import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
import com.maddyhome.idea.vim.common.InsertSequence
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.VimStateMachine import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
@ -96,7 +95,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr {
} }
// TODO: Data could be lost during visual block motion // TODO: Data could be lost during visual block motion
internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor()
internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor() internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor() internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
@ -105,7 +103,6 @@ internal var Editor.vimInitialised: Boolean by userDataOr { false }
// ------------------ Editor // ------------------ Editor
internal fun unInitializeEditor(editor: Editor) { internal fun unInitializeEditor(editor: Editor) {
editor.vimLastSelectionType = null editor.vimLastSelectionType = null
editor.vimStateMachine = null
editor.vimMorePanel = null editor.vimMorePanel = null
editor.vimExOutput = null editor.vimExOutput = null
editor.vimLastHighlighters = null editor.vimLastHighlighters = null
@ -120,7 +117,6 @@ internal var Editor.vimIncsearchCurrentMatchOffset: Int? by userData()
* @see :help visualmode() * @see :help visualmode()
*/ */
internal var Editor.vimLastSelectionType: SelectionType? by userData() internal var Editor.vimLastSelectionType: SelectionType? by userData()
internal var Editor.vimStateMachine: VimStateMachine? by userData()
internal var Editor.vimEditorGroup: Boolean by userDataOr { false } internal var Editor.vimEditorGroup: Boolean by userDataOr { false }
internal var Editor.vimHasRelativeLineNumbersInstalled: Boolean by userDataOr { false } internal var Editor.vimHasRelativeLineNumbersInstalled: Boolean by userDataOr { false }
internal var Editor.vimMorePanel: ExOutputPanel? by userData() internal var Editor.vimMorePanel: ExOutputPanel? by userData()

View File

@ -1,32 +0,0 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.helper
import com.intellij.ide.plugins.StandalonePluginUpdateChecker
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.group.NotificationService
import com.maddyhome.idea.vim.icons.VimIcons
@Service(Service.Level.APP)
internal class VimStandalonePluginUpdateChecker : StandalonePluginUpdateChecker(
VimPlugin.getPluginId(),
updateTimestampProperty = PROPERTY_NAME,
NotificationService.IDEAVIM_STICKY_GROUP,
VimIcons.IDEAVIM,
) {
override fun skipUpdateCheck(): Boolean = VimPlugin.isNotEnabled() || "dev" in VimPlugin.getVersion()
companion object {
private const val PROPERTY_NAME = "ideavim.statistics.timestamp"
val instance: VimStandalonePluginUpdateChecker = service()
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.listener
import com.intellij.execution.impl.ConsoleViewImpl
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.EditorKind
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.newapi.ij
/**
* This listener is similar to the one we introduce in vim-engine to handle focus change,
* However, in IJ we would like to start editing in some editors in INSERT mode (e.g., consoles)
* It is different to we had previously. Now we go to INSERT mode not only when we focus on the console the first time, but every time.
* Going to INSERT on every focus is easier to implement and more consistent (behavior is always the same, you don't have to remember if you are focusing a console the first time or not)
*/
class IJEditorFocusListener : EditorListener {
override fun focusGained(editor: VimEditor) {
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
// editors. This includes editors used as part of the UI, such as the VCS commit message, or used as read-only
// viewers for text output, such as log files in run configurations or the Git Console tab. And editors are used for
// interactive stdin/stdout for console-based run configurations.
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
// to INSERT mode if they are interactive editors. Recognising these can be a bit tricky.
// These additional interactive editors are not file-based, but must have a writable document. However, log output
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
// read-only and also hides the caret).
// Furthermore, interactive stdin/stdout console output in run configurations is hosted in a read-only editor, but
// it can still be edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's
// `isViewer` property and allows typing if the associated process (if any) is still running. We can get the
// editor's console view and check this ourselves, but we have to wait until the editor has finished initialising
// before it's available in user data.
// Finally, we have a special check for diff windows. If we compare against clipboard, we get a diff editor that is
// not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
val switchToInsertMode = Runnable {
val context: ExecutionContext = injector.executionContextManager.getEditorExecutionContext(editor)
VimPlugin.getChange().insertBeforeCursor(editor, context)
}
val ijEditor = editor.ij
if (!ijEditor.isViewer &&
!EditorHelper.isFileEditor(ijEditor) &&
ijEditor.document.isWritable &&
!ijEditor.inInsertMode && ijEditor.editorKind != EditorKind.DIFF
) {
switchToInsertMode.run()
}
ApplicationManager.getApplication().invokeLater {
if (ijEditor.isDisposed) return@invokeLater
val consoleView: ConsoleViewImpl? = ijEditor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW)
if (consoleView != null && consoleView.isRunning && !ijEditor.inInsertMode) {
switchToInsertMode.run()
}
}
KeyHandler.getInstance().reset(editor)
}
}

View File

@ -28,6 +28,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.ex.AnActionListener import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.impl.ScrollingModelImpl
import com.intellij.openapi.project.DumbAwareToggleAction import com.intellij.openapi.project.DumbAwareToggleAction
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
@ -36,7 +37,6 @@ import com.maddyhome.idea.vim.action.VimShortcutKeyAction
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.group.NotificationService import com.maddyhome.idea.vim.group.NotificationService
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
@ -57,6 +57,7 @@ internal object IdeaSpecifics {
private val surrounderAction = private val surrounderAction =
"com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction" "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
private var editor: Editor? = null private var editor: Editor? = null
private var caretOffset = -1
private var completionPrevDocumentLength: Int? = null private var completionPrevDocumentLength: Int? = null
private var completionPrevDocumentOffset: Int? = null private var completionPrevDocumentOffset: Int? = null
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
@ -65,6 +66,7 @@ internal object IdeaSpecifics {
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
if (hostEditor != null) { if (hostEditor != null) {
editor = hostEditor editor = hostEditor
caretOffset = hostEditor.caretModel.offset
} }
val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction
@ -96,7 +98,8 @@ internal object IdeaSpecifics {
if (VimPlugin.isNotEnabled()) return if (VimPlugin.isNotEnabled()) return
val editor = editor val editor = editor
if (editor != null && action is ChooseItemAction && injector.registerGroup.isRecording) { if (editor != null) {
if (action is ChooseItemAction && injector.registerGroup.isRecording) {
val prevDocumentLength = completionPrevDocumentLength val prevDocumentLength = completionPrevDocumentLength
val prevDocumentOffset = completionPrevDocumentOffset val prevDocumentOffset = completionPrevDocumentOffset
@ -124,7 +127,6 @@ internal object IdeaSpecifics {
} }
) { ) {
editor?.let { editor?.let {
val commandState = it.vim.vimStateMachine
it.vim.mode = Mode.NORMAL() it.vim.mode = Mode.NORMAL()
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim) VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
KeyHandler.getInstance().reset(it.vim) KeyHandler.getInstance().reset(it.vim)
@ -132,7 +134,21 @@ internal object IdeaSpecifics {
} }
//endregion //endregion
if (caretOffset != -1 && caretOffset != editor.caretModel.offset) {
val scrollModel = editor.scrollingModel as ScrollingModelImpl
if (scrollModel.isScrollingNow) {
val v = scrollModel.verticalScrollOffset
val h = scrollModel.horizontalScrollOffset
scrollModel.finishAnimation()
scrollModel.scroll(h, v)
scrollModel.finishAnimation()
}
injector.scroll.scrollCaretIntoView(editor.vim)
}
}
this.editor = null this.editor = null
this.caretOffset = -1
} }
} }

View File

@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.DocumentEx
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
import com.intellij.openapi.editor.ex.FocusChangeListener import com.intellij.openapi.editor.ex.FocusChangeListener
import com.intellij.openapi.editor.impl.EditorComponentImpl import com.intellij.openapi.editor.impl.EditorComponentImpl
import com.intellij.openapi.editor.impl.EditorImpl
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.FileEditorManagerEvent import com.intellij.openapi.fileEditor.FileEditorManagerEvent
import com.intellij.openapi.fileEditor.FileEditorManagerListener import com.intellij.openapi.fileEditor.FileEditorManagerListener
@ -45,14 +46,16 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
import com.intellij.openapi.fileEditor.impl.EditorComposite import com.intellij.openapi.fileEditor.impl.EditorComposite
import com.intellij.openapi.fileEditor.impl.EditorWindow import com.intellij.openapi.fileEditor.impl.EditorWindow
import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.rd.createLifetime
import com.intellij.openapi.rd.createNestedDisposable
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.openapi.util.removeUserData import com.intellij.openapi.util.removeUserData
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.ExceptionUtil import com.intellij.util.ExceptionUtil
import com.jetbrains.rd.util.lifetime.Lifetime
import com.maddyhome.idea.vim.EventFacade import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.KeyHandlerStateResetter
import com.maddyhome.idea.vim.VimKeyListener import com.maddyhome.idea.vim.VimKeyListener
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.VimTypedActionHandler import com.maddyhome.idea.vim.VimTypedActionHandler
@ -80,7 +83,6 @@ import com.maddyhome.idea.vim.handler.keyCheckRequests
import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener
import com.maddyhome.idea.vim.helper.GuicursorChangeListener import com.maddyhome.idea.vim.helper.GuicursorChangeListener
import com.maddyhome.idea.vim.helper.StrictMode import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.helper.VimStandalonePluginUpdateChecker
import com.maddyhome.idea.vim.helper.exitSelectMode import com.maddyhome.idea.vim.helper.exitSelectMode
import com.maddyhome.idea.vim.helper.exitVisualMode import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.forceBarCursor import com.maddyhome.idea.vim.helper.forceBarCursor
@ -96,6 +98,7 @@ import com.maddyhome.idea.vim.newapi.InsertTimeRecorder
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup import com.maddyhome.idea.vim.newapi.IjVimSearchGroup
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.inSelectMode import com.maddyhome.idea.vim.state.mode.inSelectMode
import com.maddyhome.idea.vim.state.mode.selectionType import com.maddyhome.idea.vim.state.mode.selectionType
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
@ -104,7 +107,6 @@ import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
import com.maddyhome.idea.vim.vimDisposable
import java.awt.event.MouseAdapter import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent import java.awt.event.MouseEvent
import javax.swing.SwingUtilities import javax.swing.SwingUtilities
@ -175,7 +177,7 @@ internal object VimListenerManager {
injector.listenersNotifier.macroRecordingListeners.add(macroWidgetListener) injector.listenersNotifier.macroRecordingListeners.add(macroWidgetListener)
injector.listenersNotifier.vimPluginListeners.add(macroWidgetListener) injector.listenersNotifier.vimPluginListeners.add(macroWidgetListener)
injector.listenersNotifier.myEditorListeners.add(KeyHandlerStateResetter()) injector.listenersNotifier.myEditorListeners.add(IJEditorFocusListener())
injector.listenersNotifier.myEditorListeners.add(ShowCmdWidgetUpdater()) injector.listenersNotifier.myEditorListeners.add(ShowCmdWidgetUpdater())
} }
@ -285,12 +287,10 @@ internal object VimListenerManager {
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised // TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
if (vimDisabled(editor)) return if (vimDisabled(editor)) return
// As I understand, there is no need to pass a disposable that also disposes on editor close val pluginLifetime = VimPlugin.getInstance().createLifetime()
// because all editor resources will be garbage collected anyway on editor close val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
// Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need val disposable =
// to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
// editor's disposable individually.
val disposable = editor.project?.vimDisposable ?: return
// Protect against double initialisation // Protect against double initialisation
if (editor.getUserData(editorListenersDisposableKey) != null) { if (editor.getUserData(editorListenersDisposableKey) != null) {
@ -387,6 +387,15 @@ internal object VimListenerManager {
// We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly // We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly
if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return
val newEditor = event.newEditor
if (newEditor is TextEditor) {
val editor = newEditor.editor
if (editor.isInsertMode) {
editor.vim.mode = Mode.NORMAL()
KeyHandler.getInstance().reset(editor.vim)
}
}
MotionGroup.fileEditorManagerSelectionChangedCallback(event) MotionGroup.fileEditorManagerSelectionChangedCallback(event)
FileGroup.fileEditorManagerSelectionChangedCallback(event) FileGroup.fileEditorManagerSelectionChangedCallback(event)
VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event) VimPlugin.getSearch().fileEditorManagerSelectionChangedCallback(event)
@ -458,8 +467,6 @@ internal object VimListenerManager {
event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused)) event.editor.putUserData(openingEditorKey, OpeningEditor(openingEditor, owningEditorWindow, isPreview, canBeReused))
} }
VimStandalonePluginUpdateChecker.instance.pluginUsed()
} }
override fun editorReleased(event: EditorFactoryEvent) { override fun editorReleased(event: EditorFactoryEvent) {
@ -738,7 +745,7 @@ internal object VimListenerManager {
val editor = event.editor val editor = event.editor
val commandLine = injector.commandLine.getActiveCommandLine() val commandLine = injector.commandLine.getActiveCommandLine()
if (commandLine != null) { if (commandLine != null) {
injector.processGroup.cancelExEntry(editor.vim, false) injector.processGroup.cancelExEntry(editor.vim, refocusOwningEditor = true, resetCaret = false)
} }
ExOutputModel.tryGetInstance(editor)?.close() ExOutputModel.tryGetInstance(editor)?.close()
@ -769,7 +776,7 @@ internal object VimListenerManager {
) { ) {
val commandLine = injector.commandLine.getActiveCommandLine() val commandLine = injector.commandLine.getActiveCommandLine()
if (commandLine != null) { if (commandLine != null) {
injector.processGroup.cancelExEntry(event.editor.vim, false) injector.processGroup.cancelExEntry(event.editor.vim, refocusOwningEditor = true, resetCaret = false)
} }
ExOutputModel.getInstance(event.editor).close() ExOutputModel.getInstance(event.editor).close()

View File

@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.editor.VisualPosition import com.intellij.openapi.editor.VisualPosition
import com.maddyhome.idea.vim.api.BufferPosition import com.maddyhome.idea.vim.api.BufferPosition
import com.maddyhome.idea.vim.api.CaretRegisterStorage
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
import com.maddyhome.idea.vim.api.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.LocalMarkStorage import com.maddyhome.idea.vim.api.LocalMarkStorage
import com.maddyhome.idea.vim.api.SelectionInfo import com.maddyhome.idea.vim.api.SelectionInfo
@ -29,7 +27,6 @@ import com.maddyhome.idea.vim.helper.insertHistory
import com.maddyhome.idea.vim.helper.lastSelectionInfo import com.maddyhome.idea.vim.helper.lastSelectionInfo
import com.maddyhome.idea.vim.helper.markStorage import com.maddyhome.idea.vim.helper.markStorage
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.registerStorage
import com.maddyhome.idea.vim.helper.resetVimLastColumn import com.maddyhome.idea.vim.helper.resetVimLastColumn
import com.maddyhome.idea.vim.helper.vimInsertStart import com.maddyhome.idea.vim.helper.vimInsertStart
import com.maddyhome.idea.vim.helper.vimLastColumn import com.maddyhome.idea.vim.helper.vimLastColumn
@ -41,17 +38,6 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
internal class IjVimCaret(val caret: Caret) : VimCaretBase() { internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
override val registerStorage: CaretRegisterStorage
get() {
var storage = this.caret.registerStorage
if (storage == null) {
storage = CaretRegisterStorageBase(this)
this.caret.registerStorage = storage
} else if (storage.caret != this) {
storage.caret = this
}
return storage
}
override val markStorage: LocalMarkStorage override val markStorage: LocalMarkStorage
get() { get() {
var storage = this.caret.markStorage var storage = this.caret.markStorage

View File

@ -151,21 +151,38 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
return editor.caretModel.allCarets.map { IjVimCaret(it) } return editor.caretModel.allCarets.map { IjVimCaret(it) }
} }
override var isFirstCaret = true
override var isReversingCarets = false
@Suppress("ideavimRunForEachCaret") @Suppress("ideavimRunForEachCaret")
override fun forEachCaret(action: (VimCaret) -> Unit) { override fun forEachCaret(action: (VimCaret) -> Unit) {
if (editor.vim.inBlockSelection) { if (editor.vim.inBlockSelection) {
action(IjVimCaret(editor.caretModel.primaryCaret)) action(IjVimCaret(editor.caretModel.primaryCaret))
} else { } else {
try {
editor.caretModel.runForEachCaret({ editor.caretModel.runForEachCaret({
if (it.isValid) { if (it.isValid) {
action(IjVimCaret(it)) action(IjVimCaret(it))
isFirstCaret = false
} }
}, false) }, false)
} finally {
isFirstCaret = true
}
} }
} }
override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) { override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {
editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, reverse) isReversingCarets = reverse
try {
editor.caretModel.runForEachCaret({
action(IjVimCaret(it))
isFirstCaret = false
}, reverse)
} finally {
isFirstCaret = true
isReversingCarets = false
}
} }
override fun isInForEachCaretScope(): Boolean { override fun isInForEachCaretScope(): Boolean {

View File

@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.newapi
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.components.serviceIfCreated import com.intellij.openapi.components.serviceIfCreated
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
import com.maddyhome.idea.vim.api.EngineEditorHelper import com.maddyhome.idea.vim.api.EngineEditorHelper
import com.maddyhome.idea.vim.api.ExecutionContextManager import com.maddyhome.idea.vim.api.ExecutionContextManager
@ -28,8 +27,6 @@ import com.maddyhome.idea.vim.api.VimDigraphGroup
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimEditorGroup import com.maddyhome.idea.vim.api.VimEditorGroup
import com.maddyhome.idea.vim.api.VimEnabler import com.maddyhome.idea.vim.api.VimEnabler
import com.maddyhome.idea.vim.api.VimExOutputPanel
import com.maddyhome.idea.vim.api.VimExOutputPanelService
import com.maddyhome.idea.vim.api.VimExtensionRegistrator import com.maddyhome.idea.vim.api.VimExtensionRegistrator
import com.maddyhome.idea.vim.api.VimFile import com.maddyhome.idea.vim.api.VimFile
import com.maddyhome.idea.vim.api.VimInjector import com.maddyhome.idea.vim.api.VimInjector
@ -41,6 +38,7 @@ import com.maddyhome.idea.vim.api.VimMarkService
import com.maddyhome.idea.vim.api.VimMessages import com.maddyhome.idea.vim.api.VimMessages
import com.maddyhome.idea.vim.api.VimMotionGroup import com.maddyhome.idea.vim.api.VimMotionGroup
import com.maddyhome.idea.vim.api.VimOptionGroup import com.maddyhome.idea.vim.api.VimOptionGroup
import com.maddyhome.idea.vim.api.VimOutputPanelService
import com.maddyhome.idea.vim.api.VimProcessGroup import com.maddyhome.idea.vim.api.VimProcessGroup
import com.maddyhome.idea.vim.api.VimPsiService import com.maddyhome.idea.vim.api.VimPsiService
import com.maddyhome.idea.vim.api.VimRedrawService import com.maddyhome.idea.vim.api.VimRedrawService
@ -79,9 +77,7 @@ import com.maddyhome.idea.vim.helper.IjActionExecutor
import com.maddyhome.idea.vim.helper.IjEditorHelper import com.maddyhome.idea.vim.helper.IjEditorHelper
import com.maddyhome.idea.vim.helper.IjVimStringParser import com.maddyhome.idea.vim.helper.IjVimStringParser
import com.maddyhome.idea.vim.helper.UndoRedoHelper import com.maddyhome.idea.vim.helper.UndoRedoHelper
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.history.VimHistory import com.maddyhome.idea.vim.history.VimHistory
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.macro.VimMacro import com.maddyhome.idea.vim.macro.VimMacro
import com.maddyhome.idea.vim.put.VimPut import com.maddyhome.idea.vim.put.VimPut
import com.maddyhome.idea.vim.register.VimRegisterGroup import com.maddyhome.idea.vim.register.VimRegisterGroup
@ -105,12 +101,6 @@ internal class IjVimInjector : VimInjectorBase() {
override val actionExecutor: VimActionExecutor override val actionExecutor: VimActionExecutor
get() = service<IjActionExecutor>() get() = service<IjActionExecutor>()
override val exOutputPanel: VimExOutputPanelService
get() = object : VimExOutputPanelService {
override fun getPanel(editor: VimEditor): VimExOutputPanel {
return ExOutputModel.getInstance(editor.ij)
}
}
override val historyGroup: VimHistory override val historyGroup: VimHistory
get() = service<HistoryGroup>() get() = service<HistoryGroup>()
override val extensionRegistrator: VimExtensionRegistrator override val extensionRegistrator: VimExtensionRegistrator
@ -193,6 +183,8 @@ internal class IjVimInjector : VimInjectorBase() {
get() = com.maddyhome.idea.vim.vimscript.parser.VimscriptParser get() = com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
override val commandLine: VimCommandLineService override val commandLine: VimCommandLineService
get() = service() get() = service()
override val outputPanel: VimOutputPanelService
get() = service()
override val optionGroup: VimOptionGroup override val optionGroup: VimOptionGroup
get() = service() get() = service()
@ -206,21 +198,14 @@ internal class IjVimInjector : VimInjectorBase() {
override val redrawService: VimRedrawService override val redrawService: VimRedrawService
get() = service() get() = service()
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: VimEditor): VimStateMachine { override fun commandStateFor(editor: VimEditor): VimStateMachine {
var res = editor.ij.vimStateMachine return vimState
if (res == null) {
res = VimStateMachineImpl()
editor.ij.vimStateMachine = res
}
return res
} }
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
override fun commandStateFor(editor: Any): VimStateMachine { override fun commandStateFor(editor: Any): VimStateMachine {
return when (editor) { return vimState
is VimEditor -> this.commandStateFor(editor)
is Editor -> this.commandStateFor(IjVimEditor(editor))
else -> error("Unexpected type: $editor")
}
} }
override val engineEditorHelper: EngineEditorHelper override val engineEditorHelper: EngineEditorHelper

View File

@ -58,7 +58,7 @@ public class ExOutputPanel extends JPanel {
private @Nullable LayoutManager myOldLayout = null; private @Nullable LayoutManager myOldLayout = null;
private boolean myWasOpaque = false; private boolean myWasOpaque = false;
private boolean myActive = false; public boolean myActive = false;
private static final VimLogger LOG = injector.getLogger(ExOutputPanel.class); private static final VimLogger LOG = injector.getLogger(ExOutputPanel.class);
@ -90,12 +90,16 @@ public class ExOutputPanel extends JPanel {
updateUI(); updateUI();
} }
public static @Nullable ExOutputPanel getNullablePanel(@NotNull Editor editor) {
return UserDataManager.getVimMorePanel(editor);
}
public static boolean isPanelActive(@NotNull Editor editor) { public static boolean isPanelActive(@NotNull Editor editor) {
return UserDataManager.getVimMorePanel(editor) != null; return getNullablePanel(editor) != null;
} }
public static @NotNull ExOutputPanel getInstance(@NotNull Editor editor) { public static @NotNull ExOutputPanel getInstance(@NotNull Editor editor) {
ExOutputPanel panel = UserDataManager.getVimMorePanel(editor); ExOutputPanel panel = getNullablePanel(editor);
if (panel == null) { if (panel == null) {
panel = new ExOutputPanel(editor); panel = new ExOutputPanel(editor);
UserDataManager.setVimMorePanel(editor, panel); UserDataManager.setVimMorePanel(editor, panel);
@ -192,7 +196,7 @@ public class ExOutputPanel extends JPanel {
/** /**
* Turns on the more window for the given editor * Turns on the more window for the given editor
*/ */
private void activate() { public void activate() {
JRootPane root = SwingUtilities.getRootPane(myEditor.getContentComponent()); JRootPane root = SwingUtilities.getRootPane(myEditor.getContentComponent());
myOldGlass = (JComponent)root.getGlassPane(); myOldGlass = (JComponent)root.getGlassPane();
if (myOldGlass != null) { if (myOldGlass != null) {

View File

@ -15,8 +15,8 @@ import com.maddyhome.idea.vim.api.VimCommandLineService
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.TestInputModel import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inRepeatMode
import com.maddyhome.idea.vim.helper.isCloseKeyStroke import com.maddyhome.idea.vim.helper.isCloseKeyStroke
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.ij import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.ui.ModalEntry import com.maddyhome.idea.vim.ui.ModalEntry
@ -31,7 +31,7 @@ class ExEntryPanelService : VimCommandLineService {
override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? { override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
val editor = vimEditor.ij val editor = vimEditor.ij
if (vimEditor.vimStateMachine.isDotRepeatInProgress) { if (vimEditor.inRepeatMode) {
val input = Extension.consumeString() val input = Extension.consumeString()
return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}") return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}")
} }

View File

@ -293,7 +293,7 @@ public class ExTextField extends JTextField {
clearCurrentAction(); clearCurrentAction();
Editor editor = ExEntryPanel.instance.getEditor(); Editor editor = ExEntryPanel.instance.getEditor();
if (editor != null) { if (editor != null) {
VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true); VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true, true);
} }
} }

View File

@ -0,0 +1,34 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.ui.ex
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimOutputPanel
import com.maddyhome.idea.vim.api.VimOutputPanelServiceBase
import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.newapi.ij
import java.lang.ref.WeakReference
class IjOutputPanelService : VimOutputPanelServiceBase() {
private var activeOutputPanel: VimOutputPanel? = null
override fun getCurrentOutputPanel(): VimOutputPanel? {
return activeOutputPanel?.takeIf {
(it as ExOutputModel)
it.isActive && it.editor != null
}
}
override fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
val panel = ExOutputModel(WeakReference(editor.ij))
activeOutputPanel = panel
return panel
}
}

View File

@ -24,6 +24,8 @@
serviceInterface="com.maddyhome.idea.vim.api.VimProcessGroup"/> serviceInterface="com.maddyhome.idea.vim.api.VimProcessGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.ui.ex.ExEntryPanelService" <applicationService serviceImplementation="com.maddyhome.idea.vim.ui.ex.ExEntryPanelService"
serviceInterface="com.maddyhome.idea.vim.api.VimCommandLineService"/> serviceInterface="com.maddyhome.idea.vim.api.VimCommandLineService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.ui.ex.IjOutputPanelService"
serviceInterface="com.maddyhome.idea.vim.api.VimOutputPanelService"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.DigraphGroup" <applicationService serviceImplementation="com.maddyhome.idea.vim.group.DigraphGroup"
serviceInterface="com.maddyhome.idea.vim.api.VimDigraphGroup"/> serviceInterface="com.maddyhome.idea.vim.api.VimDigraphGroup"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.group.HistoryGroup"/> <applicationService serviceImplementation="com.maddyhome.idea.vim.group.HistoryGroup"/>

View File

@ -1,12 +1,4 @@
<!-- <idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
~ Copyright 2003-2023 The IdeaVim authors
~
~ Use of this source code is governed by an MIT-style
~ license that can be found in the LICENSE.txt file or at
~ https://opensource.org/licenses/MIT.
-->
<idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude">
<name>IdeaVim</name> <name>IdeaVim</name>
<id>IdeaVIM</id> <id>IdeaVIM</id>
<description><![CDATA[ <description><![CDATA[
@ -21,7 +13,7 @@
<li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li> <li><a href="https://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
</ul> </ul>
]]></description> ]]></description>
<version>SNAPSHOT</version> <version>chylex</version>
<vendor>JetBrains</vendor> <vendor>JetBrains</vendor>
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) -->
@ -154,5 +146,6 @@
</group> </group>
<action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/> <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/>
<action id="VimJumpToSource" class="com.intellij.diff.actions.impl.OpenInEditorAction" />
</actions> </actions>
</idea-plugin> </idea-plugin>

View File

@ -10,7 +10,6 @@ package org.jetbrains.plugins.ideavim.action
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
@ -73,8 +72,6 @@ class MotionActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
doTest(listOf("12", "<Esc>"), content, content, Mode.NORMAL()) doTest(listOf("12", "<Esc>"), content, content, Mode.NORMAL())
assertPluginError(false) assertPluginError(false)
val vimCommandState = fixture.editor.vimStateMachine
kotlin.test.assertNotNull(vimCommandState)
assertEmpty(KeyHandler.getInstance().keyHandlerState.commandBuilder.keys.toList()) assertEmpty(KeyHandler.getInstance().keyHandlerState.commandBuilder.keys.toList())
} }

View File

@ -13,7 +13,6 @@ import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
@ -2173,7 +2172,7 @@ rtyfg${c}hzxc"""
val editor = configureByText(before) val editor = configureByText(before)
injector.registerGroup.storeText('*', "fgh") injector.registerGroup.storeText('*', "fgh")
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(IjVimEditor(editor), editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(IjVimEditor(editor), TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("\"*P")) typeText(injector.parser.parseKeys("\"*P"))
val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn" val after = "fg${c}hqfg${c}hwe asd zxc rty fg${c}hfgh vbn"
assertState(after) assertState(after)

View File

@ -33,7 +33,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
appReadySetup(false) appReadySetup(false)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("p")) typeText(injector.parser.parseKeys("p"))
val notification = notifications().last() val notification = notifications().last()
@ -53,7 +53,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
appReadySetup(false) appReadySetup(false)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("p")) typeText(injector.parser.parseKeys("p"))
val notifications = notifications() val notifications = notifications()
@ -71,7 +71,7 @@ class IdeaPutNotificationsTest : VimTestCase() {
appReadySetup(true) appReadySetup(true)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("p")) typeText(injector.parser.parseKeys("p"))
val notifications = EventLog.getLogModel(fixture.project).notifications val notifications = EventLog.getLogModel(fixture.project).notifications

View File

@ -88,7 +88,7 @@ class PutTestAfterCursorActionTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) .storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("p")) typeText(injector.parser.parseKeys("p"))
val after = """ val after = """
A Discovery A Discovery
@ -127,7 +127,6 @@ class PutTestAfterCursorActionTest : VimTestCase() {
val vimEditor = editor.vim val vimEditor = editor.vim
injector.registerGroup.storeText( injector.registerGroup.storeText(
vimEditor, vimEditor,
vimEditor.primaryCaret(),
before rangeOf "I found it in a legendary land\n", before rangeOf "I found it in a legendary land\n",
SelectionType.LINE_WISE, SelectionType.LINE_WISE,
false, false,
@ -157,7 +156,7 @@ class PutTestAfterCursorActionTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("vep")) typeText(injector.parser.parseKeys("vep"))
val after = """ val after = """
A Discovery A Discovery

View File

@ -31,7 +31,7 @@ class PutTextBeforeCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
injector.registerGroup.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) injector.registerGroup.storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "P")) typeText(injector.parser.parseKeys("V" + "P"))
typeText(injector.parser.parseKeys("V" + "P")) typeText(injector.parser.parseKeys("V" + "P"))
val after = """ val after = """

View File

@ -54,7 +54,7 @@ class PutViaIdeaTest : VimTestCase() {
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText("ppp") typeText("ppp")
val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land" val after = "Ilegendarylegendarylegendar${c}y found it in a legendary land"
@ -74,7 +74,6 @@ class PutViaIdeaTest : VimTestCase() {
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText( .storeText(
vimEditor, vimEditor,
vimEditor.primaryCaret(),
before rangeOf "legendary$randomUUID", before rangeOf "legendary$randomUUID",
SelectionType.CHARACTER_WISE, SelectionType.CHARACTER_WISE,
false, false,
@ -100,7 +99,6 @@ class PutViaIdeaTest : VimTestCase() {
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister().storeText( VimPlugin.getRegister().storeText(
vimEditor, vimEditor,
vimEditor.primaryCaret(),
before rangeOf "\nLorem ipsum dolor sit amet,\n", before rangeOf "\nLorem ipsum dolor sit amet,\n",
SelectionType.CHARACTER_WISE, SelectionType.CHARACTER_WISE,
false, false,

View File

@ -75,7 +75,7 @@ class PutVisualTextActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = "legendar${c}y it in a legendary land" val after = "legendar${c}y it in a legendary land"
assertState(after) assertState(after)
@ -87,7 +87,7 @@ class PutVisualTextActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "2p")) typeText(injector.parser.parseKeys("v2e" + "2p"))
val after = "legendarylegendar${c}y in a legendary land" val after = "legendarylegendar${c}y in a legendary land"
assertState(after) assertState(after)
@ -99,7 +99,7 @@ class PutVisualTextActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v$" + "2p")) typeText(injector.parser.parseKeys("v$" + "2p"))
val after = "legendarylegendar${c}y" val after = "legendarylegendar${c}y"
assertState(after) assertState(after)
@ -133,7 +133,7 @@ class PutVisualTextActionTest : VimTestCase() {
val before = "I foun${c}d it in a legendary land" val before = "I foun${c}d it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "legendary", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("vb" + "p")) typeText(injector.parser.parseKeys("vb" + "p"))
val after = "I legendar${c}y it in a legendary land" val after = "I legendar${c}y it in a legendary land"
assertState(after) assertState(after)
@ -154,7 +154,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -182,7 +182,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -210,7 +210,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -238,7 +238,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("v$" + "p")) typeText(injector.parser.parseKeys("v$" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -455,7 +455,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -491,7 +491,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("ve" + "p")) typeText(injector.parser.parseKeys("ve" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -529,7 +529,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("ve" + "2p")) typeText(injector.parser.parseKeys("ve" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -640,7 +640,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -666,7 +666,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "2p")) typeText(injector.parser.parseKeys("V" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -703,7 +703,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -876,7 +876,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -902,7 +902,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("V" + "2p")) typeText(injector.parser.parseKeys("V" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -939,7 +939,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1117,7 +1117,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1172,7 +1172,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("V" + "p")) typeText(injector.parser.parseKeys("V" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1233,7 +1233,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("V" + "2p")) typeText(injector.parser.parseKeys("V" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -1409,7 +1409,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1435,7 +1435,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("<C-V>3e2k" + "p")) typeText(injector.parser.parseKeys("<C-V>3e2k" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1461,7 +1461,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -1487,7 +1487,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "Discovery", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("<C-V>3j$" + "p")) typeText(injector.parser.parseKeys("<C-V>3j$" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1526,7 +1526,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1554,7 +1554,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "P")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "P"))
val after = """ val after = """
A Discovery A Discovery
@ -1593,7 +1593,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "2p"))
val after = """ val after = """
A Discovery A Discovery
@ -1633,7 +1633,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) typeText(injector.parser.parseKeys("<C-V>2e3j" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1672,7 +1672,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, before rangeOf "A Discovery\n", SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) typeText(injector.parser.parseKeys("<C-V>2j$" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1707,7 +1707,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e2j" + "p")) typeText(injector.parser.parseKeys("<C-V>2e2j" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1743,7 +1743,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2e3j" + "p")) typeText(injector.parser.parseKeys("<C-V>2e3j" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1779,7 +1779,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2ej" + "p")) typeText(injector.parser.parseKeys("<C-V>2ej" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1815,7 +1815,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<C-V>elj" + "p")) typeText(injector.parser.parseKeys("<C-V>elj" + "p"))
val after = """ val after = """
A Discovery A Discovery
@ -1852,7 +1852,7 @@ class PutVisualTextActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, editor.rangeOf("|found|", 2), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<C-V>2j$" + "p")) typeText(injector.parser.parseKeys("<C-V>2j$" + "p"))
val after = """ val after = """
A Discovery A Discovery

View File

@ -34,7 +34,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "2gp")) typeText(injector.parser.parseKeys("v2e" + "2gp"))
val after = "legendarylegendary$c in a legendary land" val after = "legendarylegendary$c in a legendary land"
assertState(after) assertState(after)
@ -46,7 +46,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "gp")) typeText(injector.parser.parseKeys("v2e" + "gp"))
val after = """ val after = """
@ -62,7 +62,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "gp")) typeText(injector.parser.parseKeys("V" + "gp"))
val after = "legendary\n$c" val after = "legendary\n$c"
assertState(after) assertState(after)
@ -89,7 +89,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(file) val editor = configureByText(file)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(2, 11), SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(2, 11), SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("V" + "gp")) typeText(injector.parser.parseKeys("V" + "gp"))
assertState(newFile) assertState(newFile)
} }
@ -135,7 +135,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "gP")) typeText(injector.parser.parseKeys("v2e" + "gP"))
val after = """ val after = """
@ -151,7 +151,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "2gP")) typeText(injector.parser.parseKeys("v2e" + "2gP"))
val after = "legendarylegendary$c in a legendary land" val after = "legendarylegendary$c in a legendary land"
assertState(after) assertState(after)
@ -163,7 +163,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v$" + "2gP")) typeText(injector.parser.parseKeys("v$" + "2gP"))
val after = "legendarylegendar${c}y" val after = "legendarylegendar${c}y"
assertState(after) assertState(after)
@ -175,7 +175,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}I found it in a legendary land" val before = "${c}I found it in a legendary land"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 25), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 25), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "gP")) typeText(injector.parser.parseKeys("V" + "gP"))
val after = "legendary\n$c" val after = "legendary\n$c"
assertState(after) assertState(after)
@ -274,7 +274,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.BLOCK_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.BLOCK_WISE, false)
typeText(injector.parser.parseKeys("<S-v>" + "gp")) typeText(injector.parser.parseKeys("<S-v>" + "gp"))
val after = """ val after = """
${c}fgh ${c}fgh
@ -300,7 +300,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
""".trimIndent() """.trimIndent()
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.LINE_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.LINE_WISE, false)
typeText(injector.parser.parseKeys("<C-v>" + "h" + "gp")) typeText(injector.parser.parseKeys("<C-v>" + "h" + "gp"))
val after = """ val after = """
q q
@ -323,7 +323,7 @@ class PutVisualTextMoveCursorActionTest : VimTestCase() {
val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn" val before = "${c}qwe asd ${c}zxc rty ${c}fgh vbn"
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister().storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("v2e" + "2gp")) typeText(injector.parser.parseKeys("v2e" + "2gp"))
val after = "fghfgh$c fghfgh$c fghfgh$c" val after = "fghfgh$c fghfgh$c fghfgh$c"
assertState(after) assertState(after)

View File

@ -91,7 +91,7 @@ class YankVisualActionTest : VimTestCase() {
typeText(injector.parser.parseKeys("viw" + "y")) typeText(injector.parser.parseKeys("viw" + "y"))
val editor = fixture.editor.vim val editor = fixture.editor.vim
val lastRegister = injector.registerGroup.lastRegisterChar val lastRegister = injector.registerGroup.lastRegisterChar
val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText }
kotlin.test.assertEquals(listOf("found", "was"), registers) kotlin.test.assertEquals(listOf("found", "was"), registers)
} }
@ -172,7 +172,7 @@ class YankVisualActionTest : VimTestCase() {
typeText(injector.parser.parseKeys("V" + "y")) typeText(injector.parser.parseKeys("V" + "y"))
val editor = fixture.editor.vim val editor = fixture.editor.vim
val lastRegister = injector.registerGroup.lastRegisterChar val lastRegister = injector.registerGroup.lastRegisterChar
val registers = editor.carets().map { it.registerStorage.getRegister(lastRegister)?.rawText } val registers = editor.carets().map { injector.registerGroup.getRegister(lastRegister)?.rawText }
kotlin.test.assertEquals( kotlin.test.assertEquals(
listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"), listOf("all rocks and lavender and tufted grass,\n", "hard by the torrent of a mountain pass.\n"),
registers, registers,

View File

@ -130,7 +130,7 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(commandToKeys("pu")) typeText(commandToKeys("pu"))
val after = """ val after = """
qwe qwe
@ -165,7 +165,7 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(commandToKeys("pu")) typeText(commandToKeys("pu"))
val after = """ val after = """
qwe qwe
@ -201,7 +201,7 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(commandToKeys("4pu")) typeText(commandToKeys("4pu"))
val after = """ val after = """
qwe qwe
@ -237,7 +237,7 @@ class MultipleCaretsTest : VimTestCase() {
val editor = configureByText(before) val editor = configureByText(before)
val vimEditor = editor.vim val vimEditor = editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText(commandToKeys("4pu")) typeText(commandToKeys("4pu"))
val after = """ val after = """
qwe qwe
@ -258,7 +258,7 @@ class MultipleCaretsTest : VimTestCase() {
val before = "${c}qwe\n" + "rty\n" + "as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n" val before = "${c}qwe\n" + "rty\n" + "as${c}d\n" + "fgh\n" + "zxc\n" + "vbn\n"
val editor = configureByText(before) val editor = configureByText(before)
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(editor.vim, editor.vim.primaryCaret(), TextRange(16, 19), SelectionType.CHARACTER_WISE, false) .storeText(editor.vim, TextRange(16, 19), SelectionType.CHARACTER_WISE, false)
typeText("vj") typeText("vj")
typeText(commandToKeys("pu")) typeText(commandToKeys("pu"))

View File

@ -154,6 +154,8 @@ class SetCommandTest : VimTestCase() {
// 'fileencoding' defaults to "", but is automatically detected as UTF-8 // 'fileencoding' defaults to "", but is automatically detected as UTF-8
enterCommand("set number relativenumber scrolloff nrformats") enterCommand("set number relativenumber scrolloff nrformats")
assertExOutput(" nrformats=hex scrolloff=0")
injector.outputPanel.getCurrentOutputPanel()?.close()
assertCommandOutput("set", assertCommandOutput("set",
""" """
|--- Options --- |--- Options ---
@ -169,21 +171,20 @@ class SetCommandTest : VimTestCase() {
assertCommandOutput("set all", assertCommandOutput("set all",
""" """
|--- Options --- |--- Options ---
|noargtextobj ideamarks scrolljump=1 notextobj-indent |noargtextobj ideamarks scroll=0 nosurround
|nobomb ideawrite=all scrolloff=0 textwidth=0 |nobomb ideawrite=all scrolljump=1 notextobj-entire
|nobreakindent noignorecase selectmode= timeout |nobreakindent noignorecase scrolloff=0 notextobj-indent
| colorcolumn= noincsearch shellcmdflag=-x timeoutlen=1000 | colorcolumn= noincsearch selectmode= textwidth=0
|nocommentary nolist shellxescape=@ notrackactionids |nocommentary nolist shellcmdflag=-x timeout
|nocursorline nomatchit shellxquote={ undolevels=1000 |nocursorline nomatchit shellxescape=@ timeoutlen=1000
|nodigraph maxmapdepth=20 showcmd virtualedit= |nodigraph maxmapdepth=20 shellxquote={ notrackactionids
|noexchange more showmode novisualbell |noexchange more showcmd undolevels=1000
| fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100 | fileformat=unix nomultiple-cursors showmode virtualedit=
|nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s |nogdefault noNERDTree sidescroll=0 novisualbell
|nohighlightedyank nrformats=hex nosmartcase wrap |nohighlightedyank nrformats=hex sidescrolloff=0 visualdelay=100
| history=50 nonumber nosneak wrapscan | history=50 nonumber nosmartcase whichwrap=b,s
|nohlsearch operatorfunc= startofline |nohlsearch operatorfunc= nosneak wrap
|noideaglobalmode norelativenumber nosurround |noideajoin norelativenumber startofline wrapscan
|noideajoin scroll=0 notextobj-entire
| clipboard=ideaput,autoselect,exclude:cons\|linux | clipboard=ideaput,autoselect,exclude:cons\|linux
| fileencoding=utf-8 | fileencoding=utf-8
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@ -220,6 +221,8 @@ class SetCommandTest : VimTestCase() {
// 'fileencoding' defaults to "", but is automatically detected as UTF-8 // 'fileencoding' defaults to "", but is automatically detected as UTF-8
enterCommand("set number relativenumber scrolloff nrformats") enterCommand("set number relativenumber scrolloff nrformats")
assertExOutput(" nrformats=hex scrolloff=0")
injector.outputPanel.getCurrentOutputPanel()?.close()
assertCommandOutput("set!", assertCommandOutput("set!",
""" """
|--- Options --- |--- Options ---
@ -254,7 +257,6 @@ class SetCommandTest : VimTestCase() {
|nohlsearch |nohlsearch
| ide=IntelliJ IDEA Community Edition | ide=IntelliJ IDEA Community Edition
|noideacopypreprocess |noideacopypreprocess
|noideaglobalmode
|noideajoin |noideajoin
| ideamarks | ideamarks
| idearefactormode=select | idearefactormode=select

View File

@ -435,21 +435,21 @@ class SetglobalCommandTest : VimTestCase() {
setOsSpecificOptionsToSafeValues() setOsSpecificOptionsToSafeValues()
assertCommandOutput("setglobal all", """ assertCommandOutput("setglobal all", """
|--- Global option values --- |--- Global option values ---
|noargtextobj noideajoin scroll=0 notextobj-entire |noargtextobj ideamarks scrolljump=1 notextobj-indent
|nobomb ideamarks scrolljump=1 notextobj-indent |nobomb ideawrite=all scrolloff=0 textwidth=0
|nobreakindent ideawrite=all scrolloff=0 textwidth=0 |nobreakindent noignorecase selectmode= timeout
| colorcolumn= noignorecase selectmode= timeout | colorcolumn= noincsearch shellcmdflag=-x timeoutlen=1000
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000 |nocommentary nolist shellxescape=@ notrackactionids
|nocursorline nolist shellxescape=@ notrackactionids |nocursorline nomatchit shellxquote={ undolevels=1000
|nodigraph nomatchit shellxquote={ undolevels=1000 |nodigraph maxmapdepth=20 showcmd virtualedit=
|noexchange maxmapdepth=20 showcmd virtualedit= |noexchange more showmode novisualbell
| fileencoding= more showmode novisualbell | fileencoding= nomultiple-cursors sidescroll=0 visualdelay=100
| fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100 | fileformat=unix noNERDTree sidescrolloff=0 whichwrap=b,s
|nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s |nogdefault nrformats=hex nosmartcase wrap
|nohighlightedyank nrformats=hex nosmartcase wrap |nohighlightedyank nonumber nosneak wrapscan
| history=50 nonumber nosneak wrapscan | history=50 operatorfunc= startofline
|nohlsearch operatorfunc= startofline |nohlsearch norelativenumber nosurround
|noideaglobalmode norelativenumber nosurround |noideajoin scroll=0 notextobj-entire
| clipboard=ideaput,autoselect,exclude:cons\|linux | clipboard=ideaput,autoselect,exclude:cons\|linux
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
| ide=IntelliJ IDEA Community Edition | ide=IntelliJ IDEA Community Edition
@ -523,7 +523,6 @@ class SetglobalCommandTest : VimTestCase() {
|nohlsearch |nohlsearch
| ide=IntelliJ IDEA Community Edition | ide=IntelliJ IDEA Community Edition
|noideacopypreprocess |noideacopypreprocess
|noideaglobalmode
|noideajoin |noideajoin
| ideamarks | ideamarks
| idearefactormode=select | idearefactormode=select

View File

@ -489,21 +489,20 @@ class SetlocalCommandTest : VimTestCase() {
setOsSpecificOptionsToSafeValues() setOsSpecificOptionsToSafeValues()
assertCommandOutput("setlocal all", """ assertCommandOutput("setlocal all", """
|--- Local option values --- |--- Local option values ---
|noargtextobj ideamarks scroll=0 notextobj-entire |noargtextobj ideamarks norelativenumber startofline
|nobomb idearefactormode= scrolljump=1 notextobj-indent |nobomb idearefactormode= scroll=0 nosurround
|nobreakindent ideawrite=all scrolloff=-1 textwidth=0 |nobreakindent ideawrite=all scrolljump=1 notextobj-entire
| colorcolumn= noignorecase selectmode= timeout | colorcolumn= noignorecase scrolloff=-1 notextobj-indent
|nocommentary noincsearch shellcmdflag=-x timeoutlen=1000 |nocommentary noincsearch selectmode= textwidth=0
|nocursorline nolist shellxescape=@ notrackactionids |nocursorline nolist shellcmdflag=-x timeout
|nodigraph nomatchit shellxquote={ virtualedit= |nodigraph nomatchit shellxescape=@ timeoutlen=1000
|noexchange maxmapdepth=20 showcmd novisualbell |noexchange maxmapdepth=20 shellxquote={ notrackactionids
| fileformat=unix more showmode visualdelay=100 | fileformat=unix more showcmd virtualedit=
|nogdefault nomultiple-cursors sidescroll=0 whichwrap=b,s |nogdefault nomultiple-cursors showmode novisualbell
|nohighlightedyank noNERDTree sidescrolloff=-1 wrap |nohighlightedyank noNERDTree sidescroll=0 visualdelay=100
| history=50 nrformats=hex nosmartcase wrapscan | history=50 nrformats=hex sidescrolloff=-1 whichwrap=b,s
|nohlsearch nonumber nosneak |nohlsearch nonumber nosmartcase wrap
|noideaglobalmode operatorfunc= startofline |--ideajoin operatorfunc= nosneak wrapscan
|--ideajoin norelativenumber nosurround
| clipboard=ideaput,autoselect,exclude:cons\|linux | clipboard=ideaput,autoselect,exclude:cons\|linux
| fileencoding=utf-8 | fileencoding=utf-8
| guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
@ -577,7 +576,6 @@ class SetlocalCommandTest : VimTestCase() {
|nohlsearch |nohlsearch
| ide=IntelliJ IDEA Community Edition | ide=IntelliJ IDEA Community Edition
|--ideacopypreprocess |--ideacopypreprocess
|noideaglobalmode
|--ideajoin |--ideajoin
| ideamarks | ideamarks
| idearefactormode= | idearefactormode=

View File

@ -9,7 +9,6 @@
package org.jetbrains.plugins.ideavim.ex.implementation.commands package org.jetbrains.plugins.ideavim.ex.implementation.commands
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.register.RegisterConstants import com.maddyhome.idea.vim.register.RegisterConstants
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -303,33 +302,4 @@ class YankLinesCommandTest : VimTestCase() {
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. |Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|""".trimMargin()) |""".trimMargin())
} }
@Test
fun `test multicaret yank`() {
configureByText(
"""
|Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|${c}Morbi nec luctus tortor, id venenatis lacus.
|${c}Nunc sit amet tellus vel purus cursus posuere et at purus.
|${c}Ut id dapibus augue.
|Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|Pellentesque orci dolor, tristique quis rutrum non, scelerisque id dui.
""".trimMargin()
)
enterCommand("y")
val carets = fixture.editor.vim.carets()
assertEquals(3, carets.size)
assertEquals(
"Morbi nec luctus tortor, id venenatis lacus.\n",
carets[0].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text
)
assertEquals(
"Nunc sit amet tellus vel purus cursus posuere et at purus.\n",
carets[1].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text
)
assertEquals(
"Ut id dapibus augue.\n",
carets[2].registerStorage.getRegister(RegisterConstants.UNNAMED_REGISTER)?.text
)
}
} }

View File

@ -8,6 +8,7 @@
package org.jetbrains.plugins.ideavim.ex.implementation.statements package org.jetbrains.plugins.ideavim.ex.implementation.statements
import com.maddyhome.idea.vim.api.injector
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
@ -55,6 +56,7 @@ class FunctionDeclarationTest : VimTestCase() {
) )
typeText(commandToKeys("echo F1()")) typeText(commandToKeys("echo F1()"))
assertExOutput("5550") assertExOutput("5550")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("555") assertExOutput("555")
@ -203,10 +205,13 @@ class FunctionDeclarationTest : VimTestCase() {
), ),
) )
typeText(commandToKeys("echo F1()")) typeText(commandToKeys("echo F1()"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("1") assertExOutput("1")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("2") assertExOutput("2")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("3") assertExOutput("3")
@ -230,9 +235,11 @@ class FunctionDeclarationTest : VimTestCase() {
), ),
) )
typeText(commandToKeys("echo F1()")) typeText(commandToKeys("echo F1()"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("1") assertExOutput("1")
typeText(commandToKeys("delf! F1")) typeText(commandToKeys("delf! F1"))
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("2") assertExOutput("2")
@ -260,10 +267,12 @@ class FunctionDeclarationTest : VimTestCase() {
assertPluginError(true) assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: x") assertPluginErrorMessageContains("E121: Undefined variable: x")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F2()")) typeText(commandToKeys("echo F2()"))
assertExOutput("10") assertExOutput("10")
assertPluginError(false) assertPluginError(false)
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo F1()")) typeText(commandToKeys("echo F1()"))
assertPluginError(true) assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: x") assertPluginErrorMessageContains("E121: Undefined variable: x")
@ -289,6 +298,7 @@ class FunctionDeclarationTest : VimTestCase() {
assertPluginError(true) assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: unknownVar") assertPluginErrorMessageContains("E121: Undefined variable: unknownVar")
injector.outputPanel.getCurrentOutputPanel()?.close()
typeText(commandToKeys("echo x")) typeText(commandToKeys("echo x"))
assertExOutput("10") assertExOutput("10")
assertPluginError(false) assertPluginError(false)
@ -474,7 +484,12 @@ class FunctionDeclarationTest : VimTestCase() {
) )
typeText(commandToKeys("1,4call F1()")) typeText(commandToKeys("1,4call F1()"))
assertPluginError(false) assertPluginError(false)
assertExOutput("1:4") assertExOutput("""
1:4
1:4
1:4
1:4
""".trimIndent())
assertState( assertState(
""" """
----- -----

View File

@ -50,7 +50,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("griw")) typeText(injector.parser.parseKeys("griw"))
assertState("one on${c}e three") assertState("one on${c}e three")
assertEquals("one", VimPlugin.getRegister().lastRegister?.text) assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
@ -170,7 +170,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("3griw")) typeText(injector.parser.parseKeys("3griw"))
assertState("one on${c}e four") assertState("one on${c}e four")
assertEquals("one", VimPlugin.getRegister().lastRegister?.text) assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
@ -184,7 +184,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("griw")) typeText(injector.parser.parseKeys("griw"))
assertState("one two one four") assertState("one two one four")
assertEquals("one", VimPlugin.getRegister().lastRegister?.text) assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
@ -197,7 +197,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "one", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("griw" + "w" + ".")) typeText(injector.parser.parseKeys("griw" + "w" + "."))
assertState("one one on${c}e four") assertState("one one on${c}e four")
assertEquals("one", VimPlugin.getRegister().lastRegister?.text) assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
@ -247,7 +247,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("grr")) typeText(injector.parser.parseKeys("grr"))
assertState( assertState(
""" """
@ -414,7 +414,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("viw" + "gr")) typeText(injector.parser.parseKeys("viw" + "gr"))
assertState( assertState(
""" """
@ -485,7 +485,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
configureByText(text) configureByText(text)
val vimEditor = fixture.editor.vim val vimEditor = fixture.editor.vim
VimPlugin.getRegister() VimPlugin.getRegister()
.storeText(vimEditor, vimEditor.primaryCaret(), text rangeOf "legendary", SelectionType.CHARACTER_WISE, false) .storeText(vimEditor, text rangeOf "legendary", SelectionType.CHARACTER_WISE, false)
typeText(injector.parser.parseKeys("V" + "gr")) typeText(injector.parser.parseKeys("V" + "gr"))
assertState( assertState(
""" """

View File

@ -16,7 +16,6 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.editor.LogicalPosition
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.common.CharacterPosition import com.maddyhome.idea.vim.common.CharacterPosition
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.register.RegisterConstants.ALTERNATE_BUFFER_REGISTER import com.maddyhome.idea.vim.register.RegisterConstants.ALTERNATE_BUFFER_REGISTER
import com.maddyhome.idea.vim.register.RegisterConstants.BLACK_HOLE_REGISTER import com.maddyhome.idea.vim.register.RegisterConstants.BLACK_HOLE_REGISTER
@ -168,7 +167,7 @@ object NeovimTesting {
fun vimMode() = neovimApi.mode.get().mode fun vimMode() = neovimApi.mode.get().mode
private fun assertMode(editor: Editor) { private fun assertMode(editor: Editor) {
val ideavimState = editor.vim.vimStateMachine.mode.toVimNotation() val ideavimState = editor.vim.mode.toVimNotation()
val neovimState = vimMode() val neovimState = vimMode()
assertEquals(neovimState, ideavimState) assertEquals(neovimState, ideavimState)
} }

View File

@ -33,7 +33,6 @@ class TestOptionConstants {
const val whichwrap = "whichwrap" const val whichwrap = "whichwrap"
// IdeaVim specific // IdeaVim specific
const val ideaglobalmode = "ideaglobalmode"
const val ideatracetime = "ideatracetime" const val ideatracetime = "ideatracetime"
} }
} }

View File

@ -128,6 +128,7 @@ abstract class VimTestCase {
KeyHandler.getInstance().fullReset(editor.vim) KeyHandler.getInstance().fullReset(editor.vim)
} }
KeyHandler.getInstance().keyHandlerState.reset(Mode.NORMAL()) KeyHandler.getInstance().keyHandlerState.reset(Mode.NORMAL())
injector.vimState.reset()
resetAllOptions() resetAllOptions()
VimPlugin.getKey().resetKeyMappings() VimPlugin.getKey().resetKeyMappings()
VimPlugin.getSearch().resetState() VimPlugin.getSearch().resetState()
@ -221,6 +222,7 @@ abstract class VimTestCase {
VimPlugin.getChange().resetRepeat() VimPlugin.getChange().resetRepeat()
VimPlugin.getKey().savedShortcutConflicts.clear() VimPlugin.getKey().savedShortcutConflicts.clear()
assertTrue(KeyHandler.getInstance().keyStack.isEmpty()) assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
injector.outputPanel.getCurrentOutputPanel()?.close()
// Tear down neovim // Tear down neovim
NeovimTesting.tearDown(testInfo) NeovimTesting.tearDown(testInfo)

View File

@ -156,7 +156,6 @@ private class OptionsVerificator : BeforeTestExecutionCallback, AfterTestExecuti
val LOG by lazy { vimLogger<OptionsVerificator>() } val LOG by lazy { vimLogger<OptionsVerificator>() }
private val ignored = setOf( private val ignored = setOf(
TestOptionConstants.guicursor, TestOptionConstants.guicursor,
TestOptionConstants.ideaglobalmode,
TestOptionConstants.ideatracetime, TestOptionConstants.ideatracetime,
TestIjOptionConstants.ideavimsupport, TestIjOptionConstants.ideavimsupport,
TestOptionConstants.maxmapdepth, TestOptionConstants.maxmapdepth,

View File

@ -27,6 +27,9 @@ class TestInjector(val injector: VimInjector) : VimInjector by injector {
tracers[key] = collector tracers[key] = collector
} }
override val vimState
get() = injector.vimState
override val optionGroup: VimOptionGroup override val optionGroup: VimOptionGroup
get() { get() {
val tracer = tracers[OptionsTracer] as? OptionsTraceCollector val tracer = tracers[OptionsTracer] as? OptionsTraceCollector

View File

@ -1,5 +1,3 @@
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
/* /*
* Copyright 2003-2024 The IdeaVim authors * Copyright 2003-2024 The IdeaVim authors
* *
@ -11,20 +9,16 @@ import org.jetbrains.intellij.platform.gradle.TestFrameworkType
plugins { plugins {
id("java") id("java")
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.intellij.platform.module") id("org.jetbrains.intellij")
} }
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaType: String by project
val ideaVersion: String by project val ideaVersion: String by project
val javaVersion: String by project val javaVersion: String by project
repositories { repositories {
mavenCentral() mavenCentral()
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
intellijPlatform {
defaultRepositories()
}
} }
dependencies { dependencies {
@ -32,24 +26,35 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project testImplementation(testFixtures(project(":"))) // The root project
intellijPlatform {
create(ideaType, ideaVersion)
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
bundledPlugins("com.intellij.java", "org.jetbrains.plugins.yaml")
instrumentationTools()
}
}
intellijPlatform {
buildSearchableOptions = false
} }
tasks { tasks {
test { test {
useJUnitPlatform() useJUnitPlatform()
} }
verifyPlugin {
enabled = false
}
publishPlugin {
enabled = false
}
runIde {
enabled = false
}
runPluginVerifier {
enabled = false
}
}
intellij {
version.set(ideaVersion)
type.set("IC")
// Yaml is only used for testing. It's part of the IdeaIC distribution, but needs to be included as a reference
plugins.set(listOf("java", "yaml"))
} }
java { java {

View File

@ -1,22 +1,15 @@
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform
plugins { plugins {
java java
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.intellij.platform.module") id("org.jetbrains.intellij")
} }
repositories { repositories {
mavenCentral() mavenCentral()
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
intellijPlatform {
defaultRepositories()
}
} }
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaType: String by project
val ideaVersion: String by project val ideaVersion: String by project
val javaVersion: String by project val javaVersion: String by project
@ -25,35 +18,42 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project testImplementation(testFixtures(project(":"))) // The root project
intellijPlatform {
create(ideaType, ideaVersion)
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
instrumentationTools()
}
}
intellijPlatform {
buildSearchableOptions = false
} }
tasks { tasks {
// This task is disabled because it should be excluded from `gradle test` run (because it's slow) // This task is disabled because it should be excluded from `gradle test` run (because it's slow)
// I didn't find a better way to exclude except disabling and defining a new task with a different name // I didn't find a better way to exclude except disabling and defining a new task with a different name
// Note that useJUnitTestPlatform() is required to prevent red code
test { test {
enabled = false enabled = false
useJUnitPlatform() useJUnitPlatform()
} }
// The `test` task is automatically set up with IntelliJ goodness. A custom test task needs to be configured for it register<Test>("testLongRunning") {
val testLongRunning by intellijPlatformTesting.testIde.registering {
task {
group = "verification" group = "verification"
useJUnitPlatform() useJUnitPlatform()
} }
verifyPlugin {
enabled = false
} }
publishPlugin {
enabled = false
}
runIde {
enabled = false
}
runPluginVerifier {
enabled = false
}
}
intellij {
version.set(ideaVersion)
type.set("IC")
plugins.set(listOf("java"))
} }
java { java {

View File

@ -1,22 +1,15 @@
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform
plugins { plugins {
java java
kotlin("jvm") kotlin("jvm")
id("org.jetbrains.intellij.platform.module") id("org.jetbrains.intellij")
} }
repositories { repositories {
mavenCentral() mavenCentral()
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
intellijPlatform {
defaultRepositories()
}
} }
val kotlinVersion: String by project val kotlinVersion: String by project
val ideaType: String by project
val ideaVersion: String by project val ideaVersion: String by project
val javaVersion: String by project val javaVersion: String by project
@ -25,18 +18,6 @@ dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
testImplementation(testFixtures(project(":"))) // The root project testImplementation(testFixtures(project(":"))) // The root project
intellijPlatform {
create(ideaType, ideaVersion)
bundledPlugins("com.intellij.java")
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
instrumentationTools()
}
}
intellijPlatform {
buildSearchableOptions = false
} }
tasks { tasks {
@ -47,13 +28,32 @@ tasks {
enabled = false enabled = false
} }
// The `test` task is automatically set up with IntelliJ goodness. A custom test task needs to be configured for it register<Test>("testPropertyBased") {
val testPropertyBased by intellijPlatformTesting.testIde.registering {
task {
group = "verification" group = "verification"
useJUnitPlatform() useJUnitPlatform()
} }
verifyPlugin {
enabled = false
} }
publishPlugin {
enabled = false
}
runIde {
enabled = false
}
runPluginVerifier {
enabled = false
}
}
intellij {
version.set(ideaVersion)
type.set("IC")
plugins.set(listOf("java"))
} }
java { java {

View File

@ -16,12 +16,6 @@ plugins {
antlr antlr
} }
val sourcesJarArtifacts by configurations.registering {
attributes {
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
}
}
val kotlinVersion: String by project val kotlinVersion: String by project
val kotlinxSerializationVersion: String by project val kotlinxSerializationVersion: String by project
@ -103,8 +97,6 @@ java {
withJavadocJar() withJavadocJar()
} }
artifacts.add(sourcesJarArtifacts.name, tasks.named("sourcesJar"))
val spaceUsername: String by project val spaceUsername: String by project
val spacePassword: String by project val spacePassword: String by project
val engineVersion: String by project val engineVersion: String by project

View File

@ -18,11 +18,9 @@ import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.MappingProcessor import com.maddyhome.idea.vim.command.MappingProcessor
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.CurrentCommandState import com.maddyhome.idea.vim.common.CurrentCommandState
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.diagnostic.VimLogger import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.trace import com.maddyhome.idea.vim.diagnostic.trace
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.toMappingMode import com.maddyhome.idea.vim.impl.state.toMappingMode
import com.maddyhome.idea.vim.key.CommandPartNode import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.KeyConsumer import com.maddyhome.idea.vim.key.KeyConsumer
@ -167,7 +165,7 @@ class KeyHandler {
if (commandBuilder.isReady) { if (commandBuilder.isReady) {
logger.trace("Ready command builder. Execute command.") logger.trace("Ready command builder. Execute command.")
executeCommand(editor, context, editor.vimStateMachine, keyState) executeCommand(editor, context, injector.vimState, keyState)
} }
// Don't record the keystroke that stops the recording (unmapped this is `q`) // Don't record the keystroke that stops the recording (unmapped this is `q`)
@ -186,7 +184,7 @@ class KeyHandler {
logger.trace("Command builder is set to BAD") logger.trace("Command builder is set to BAD")
keyState.commandBuilder.commandState = CurrentCommandState.BAD_COMMAND keyState.commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
editor.resetOpPending() editor.resetOpPending()
editor.vimStateMachine.resetRegisterPending() injector.vimState.resetRegisterPending()
editor.isReplaceCharacter = false editor.isReplaceCharacter = false
reset(keyState, editor.mode) reset(keyState, editor.mode)
} }
@ -267,6 +265,7 @@ class KeyHandler {
*/ */
fun reset(editor: VimEditor) { fun reset(editor: VimEditor) {
logger.trace { "Reset is executed" } logger.trace { "Reset is executed" }
editor.resetOpPending()
keyHandlerState.partialReset(editor.mode) keyHandlerState.partialReset(editor.mode)
keyHandlerState.commandBuilder.resetAll(getKeyRoot(editor.mode.toMappingMode())) keyHandlerState.commandBuilder.resetAll(getKeyRoot(editor.mode.toMappingMode()))
} }
@ -298,7 +297,7 @@ class KeyHandler {
injector.messages.clearError() injector.messages.clearError()
editor.mode = Mode.NORMAL() editor.mode = Mode.NORMAL()
editor.vimStateMachine.executingCommand = null injector.vimState.executingCommand = null
keyHandlerState.digraphSequence.reset() keyHandlerState.digraphSequence.reset()
reset(keyHandlerState, editor.mode) reset(keyHandlerState, editor.mode)
@ -322,7 +321,7 @@ class KeyHandler {
val operatorArguments: OperatorArguments, val operatorArguments: OperatorArguments,
) : Runnable { ) : Runnable {
override fun run() { override fun run() {
val editorState = VimStateMachine.getInstance(editor) val editorState = injector.vimState
keyState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND keyState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
val register = cmd.register val register = cmd.register
if (register != null) { if (register != null) {
@ -492,15 +491,4 @@ sealed interface KeyProcessResult {
} }
} }
class KeyHandlerStateResetter : EditorListener {
override fun focusGained(editor: VimEditor) {
KeyHandler.getInstance().reset(editor)
}
override fun focusLost(editor: VimEditor) {
// We do not reset the KeyHandler state here, because a command or a search prompt is not considered to be part of the
// editor, and resetting state would break the search functionality.
}
}
typealias KeyProcessing = (KeyHandlerState, VimEditor, ExecutionContext) -> Unit typealias KeyProcessing = (KeyHandlerState, VimEditor, ExecutionContext) -> Unit

View File

@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["U", "<C-R>"], modes = [Mode.NORMAL, Mode.VISUAL])
class RedoAction : VimActionHandler.SingleExecution() { class RedoAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED

View File

@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["u", "<Undo>"], modes = [Mode.NORMAL, Mode.VISUAL])
class UndoAction : VimActionHandler.SingleExecution() { class UndoAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.action.change.change package com.maddyhome.idea.vim.action.change.change
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
/** /**
* @author vlan * @author vlan
*/ */
@CommandOrMotion(keys = ["u"], modes = [Mode.VISUAL]) @CommandOrMotion(keys = [], modes = [])
class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.action.change.change package com.maddyhome.idea.vim.action.change.change
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@ -22,7 +21,7 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
/** /**
* @author vlan * @author vlan
*/ */
@CommandOrMotion(keys = ["U"], modes = [Mode.VISUAL]) @CommandOrMotion(keys = [], modes = [])
class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE

View File

@ -16,7 +16,6 @@ import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.vimStateMachine
import java.util.* import java.util.*
@CommandOrMotion(keys = ["<Insert>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Insert>"], modes = [Mode.INSERT])
@ -38,5 +37,5 @@ class InsertInsertAction : VimActionHandler.SingleExecution() {
private fun processInsert(editor: VimEditor) { private fun processInsert(editor: VimEditor) {
editor.insertMode = !editor.insertMode editor.insertMode = !editor.insertMode
editor.vimStateMachine.toggleInsertOverwrite() editor.toggleInsertOverwrite()
} }

View File

@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.copy
import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
@ -36,7 +35,15 @@ sealed class PutTextBaseAction(
val count = operatorArguments.count1 val count = operatorArguments.count1
val sortedCarets = editor.sortedCarets() val sortedCarets = editor.sortedCarets()
return if (sortedCarets.size > 1) { return if (sortedCarets.size > 1) {
val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(it, count) } val putData = getPutData(count)
val splitText = putData.textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty)
val caretToPutData = if (splitText != null && splitText.size == sortedCarets.size) {
sortedCarets.mapIndexed { index, caret -> caret to putData.copy(textData = putData.textData.copy(rawText = splitText[splitText.lastIndex - index])) }.toMap()
} else {
sortedCarets.associateWith { putData }
}
var result = true var result = true
injector.application.runWriteAction { injector.application.runWriteAction {
caretToPutData.forEach { caretToPutData.forEach {
@ -45,20 +52,18 @@ sealed class PutTextBaseAction(
} }
result result
} else { } else {
val putData = getPutDataForCaret(sortedCarets.single(), count) injector.put.putText(editor, context, getPutData(count), operatorArguments)
injector.put.putText(editor, context, putData, operatorArguments)
} }
} }
private fun getPutDataForCaret(caret: ImmutableVimCaret, count: Int): PutData { private fun getPutData(count: Int): PutData {
val registerService = injector.registerGroup return PutData(getRegisterTextData(), null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
val registerChar = if (caret.editor.carets().size == 1) {
registerService.currentRegister
} else {
registerService.getCurrentRegisterForMulticaret()
} }
val register = caret.registerStorage.getRegister(registerChar) }
val textData = register?.let {
fun getRegisterTextData(): TextData? {
val register = injector.registerGroup.getRegister(injector.registerGroup.currentRegister)
return register?.let {
TextData( TextData(
register.text ?: injector.parser.toPrintableString(register.keys), register.text ?: injector.parser.toPrintableString(register.keys),
register.type, register.type,
@ -66,8 +71,6 @@ sealed class PutTextBaseAction(
register.name, register.name,
) )
} }
return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
}
} }
@CommandOrMotion(keys = ["p"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["p"], modes = [Mode.NORMAL])

View File

@ -41,7 +41,21 @@ sealed class PutVisualTextBaseAction(
): Boolean { ): Boolean {
if (caretsAndSelections.isEmpty()) return false if (caretsAndSelections.isEmpty()) return false
val count = cmd.count val count = cmd.count
val caretToPutData = editor.sortedCarets().associateWith { getPutDataForCaret(it, caretsAndSelections[it], count) } val sortedCarets = editor.sortedCarets()
val textData = getRegisterTextData()
val splitText = textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty)
val caretToTextData = if (splitText != null && splitText.size == sortedCarets.size) {
sortedCarets.mapIndexed { index, caret -> caret to textData.copy(rawText = splitText[splitText.lastIndex - index]) }.toMap()
} else {
sortedCarets.associateWith { textData }
}
val caretToPutData = caretToTextData.mapValues { (caret, textData) ->
getPutDataForCaret(textData, caret, caretsAndSelections[caret], count)
}
injector.registerGroup.resetRegister() injector.registerGroup.resetRegister()
var result = true var result = true
injector.application.runWriteAction { injector.application.runWriteAction {
@ -52,17 +66,7 @@ sealed class PutVisualTextBaseAction(
return result return result
} }
private fun getPutDataForCaret(caret: VimCaret, selection: VimSelection?, count: Int): PutData { private fun getPutDataForCaret(textData: PutData.TextData?, caret: VimCaret, selection: VimSelection?, count: Int): PutData {
val lastRegisterChar = injector.registerGroup.lastRegisterChar
val register = caret.registerStorage.getRegister(lastRegisterChar)
val textData = register?.let {
PutData.TextData(
register.text ?: injector.parser.toPrintableString(register.keys),
register.type,
register.transferableData,
register.name,
)
}
val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) } val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) }
return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText) return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText)
} }

View File

@ -82,6 +82,13 @@ sealed class TillCharacterMotion(
) )
} }
injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character) injector.motion.setLastFTCmd(tillCharacterMotionType, argument.character)
val offset = if (!finishBeforeCharacter) ""
else if (direction == Direction.FORWARDS) "s-1"
else "s+1"
injector.searchGroup.setLastSearchState(argument.character.let { if (it == '.') "\\." else it.toString() }, offset, direction)
return res.toMotionOrError() return res.toMotionOrError()
} }
} }

View File

@ -18,7 +18,6 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.pushVisualMode import com.maddyhome.idea.vim.helper.pushVisualMode
import com.maddyhome.idea.vim.helper.setSelectMode import com.maddyhome.idea.vim.helper.setSelectMode
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
/** /**
@ -42,8 +41,7 @@ class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
companion object { companion object {
fun toggleMode(editor: VimEditor) { fun toggleMode(editor: VimEditor) {
val commandState = editor.vimStateMachine val myMode = editor.mode
val myMode = commandState.mode
if (myMode is com.maddyhome.idea.vim.state.mode.Mode.VISUAL) { if (myMode is com.maddyhome.idea.vim.state.mode.Mode.VISUAL) {
editor.setSelectMode(myMode.selectionType) editor.setSelectMode(myMode.selectionType)
if (myMode.selectionType != SelectionType.LINE_WISE) { if (myMode.selectionType != SelectionType.LINE_WISE) {

View File

@ -51,10 +51,6 @@ open class GlobalOptions(scope: OptionAccessScope): OptionsPropertiesBase(scope)
// IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties // IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties
// This is an experimental option that enables global mode for the editor. However,
// for the moment it has issues and there is no quality garantee if this option is enabled
var ideaglobalmode: Boolean by optionProperty(Options.ideaglobalmode)
// Temporary flags for work-in-progress behaviour. Hidden from the output of `:set all` // Temporary flags for work-in-progress behaviour. Hidden from the output of `:set all`
var ideastrictmode: Boolean by optionProperty(Options.ideastrictmode) var ideastrictmode: Boolean by optionProperty(Options.ideastrictmode)
var ideatracetime: Boolean by optionProperty(Options.ideatracetime) var ideatracetime: Boolean by optionProperty(Options.ideatracetime)

View File

@ -320,7 +320,6 @@ object Options {
) )
// IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties // IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties
val ideaglobalmode: ToggleOption = addOption(ToggleOption("ideaglobalmode", GLOBAL, "ideaglobalmode", false))
// Temporary feature flags for work-in-progress behaviour, diagnostic switches, etc. Hidden from the output of `:set all` // Temporary feature flags for work-in-progress behaviour, diagnostic switches, etc. Hidden from the output of `:set all`
val ideastrictmode: ToggleOption = addOption(ToggleOption("ideastrictmode", GLOBAL, "ideastrictmode", false, isHidden = true)) val ideastrictmode: ToggleOption = addOption(ToggleOption("ideastrictmode", GLOBAL, "ideastrictmode", false, isHidden = true))

View File

@ -9,20 +9,16 @@
package com.maddyhome.idea.vim.api package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.common.LiveRange import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.visual.VisualChange import com.maddyhome.idea.vim.group.visual.VisualChange
import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset
import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret
import com.maddyhome.idea.vim.handler.Motion import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.helper.StrictMode import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.helper.exitVisualMode import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.inBlockSelection import com.maddyhome.idea.vim.state.mode.inBlockSelection
import com.maddyhome.idea.vim.state.mode.inCommandLineMode import com.maddyhome.idea.vim.state.mode.inCommandLineMode
import com.maddyhome.idea.vim.state.mode.inSelectMode import com.maddyhome.idea.vim.state.mode.inSelectMode
import com.maddyhome.idea.vim.state.mode.inVisualMode import com.maddyhome.idea.vim.state.mode.inVisualMode
import javax.swing.KeyStroke
/** /**
* Immutable interface of the caret. Immutable caret is an important concept of Fleet. * Immutable interface of the caret. Immutable caret is an important concept of Fleet.
@ -61,7 +57,6 @@ interface ImmutableVimCaret {
fun hasSelection(): Boolean fun hasSelection(): Boolean
var lastSelectionInfo: SelectionInfo var lastSelectionInfo: SelectionInfo
val registerStorage: CaretRegisterStorage
val markStorage: LocalMarkStorage val markStorage: LocalMarkStorage
} }
@ -147,21 +142,3 @@ fun VimCaret.moveToMotion(motion: Motion): VimCaret {
this this
} }
} }
interface CaretRegisterStorage {
val caret: ImmutableVimCaret
/**
* Stores text to caret's recordable (named/numbered/unnamed) register
*/
fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean
/**
* Gets text from caret's recordable register
* If the register is not recordable - global text state will be returned
*/
fun getRegister(r: Char): Register?
fun setKeys(register: Char, keys: List<KeyStroke>)
fun saveRegister(r: Char, register: Register)
}

View File

@ -8,75 +8,4 @@
package com.maddyhome.idea.vim.api package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.register.RegisterConstants
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
import javax.swing.KeyStroke
abstract class VimCaretBase : VimCaret abstract class VimCaretBase : VimCaret
open class CaretRegisterStorageBase(override var caret: ImmutableVimCaret) : CaretRegisterStorage, VimRegisterGroupBase() {
companion object {
private const val ALLOWED_TO_STORE_REGISTERS = RegisterConstants.RECORDABLE_REGISTERS +
RegisterConstants.SMALL_DELETION_REGISTER +
RegisterConstants.BLACK_HOLE_REGISTER +
RegisterConstants.LAST_INSERTED_TEXT_REGISTER +
RegisterConstants.LAST_SEARCH_REGISTER
}
override var lastRegisterChar: Char
get() {
return injector.registerGroup.lastRegisterChar
}
set(_) {}
override var isRegisterSpecifiedExplicitly: Boolean
get() {
return injector.registerGroup.isRegisterSpecifiedExplicitly
}
set(_) {}
override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean {
val registerChar = if (caret.editor.carets().size == 1) currentRegister else getCurrentRegisterForMulticaret()
if (caret.isPrimary) {
val registerService = injector.registerGroup
registerService.lastRegisterChar = registerChar
return registerService.storeText(editor, caret, range, type, isDelete)
} else {
if (!ALLOWED_TO_STORE_REGISTERS.contains(registerChar)) {
return false
}
val text = preprocessTextBeforeStoring(editor.getText(range), type)
return storeTextInternal(editor, caret, range, text, type, registerChar, isDelete)
}
}
override fun getRegister(r: Char): Register? {
if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
return injector.registerGroup.getRegister(r)
}
return super.getRegister(r) ?: injector.registerGroup.getRegister(r)
}
override fun setKeys(register: Char, keys: List<KeyStroke>) {
if (caret.isPrimary) {
injector.registerGroup.setKeys(register, keys)
}
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) {
return
}
return super.setKeys(register, keys)
}
override fun saveRegister(r: Char, register: Register) {
if (caret.isPrimary) {
injector.registerGroup.saveRegister(r, register)
}
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
return
}
return super.saveRegister(r, register)
}
}

View File

@ -146,7 +146,7 @@ interface VimChangeGroup {
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
) )
fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret
fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret fun insertText(editor: VimEditor, caret: VimCaret, str: String): VimCaret

View File

@ -26,13 +26,11 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.CharacterHelper.charType import com.maddyhome.idea.vim.helper.CharacterHelper.charType
import com.maddyhome.idea.vim.helper.StrictMode import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.helper.usesVirtualSpace import com.maddyhome.idea.vim.helper.usesVirtualSpace
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_END
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_START import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_START
import com.maddyhome.idea.vim.register.RegisterConstants.LAST_INSERTED_TEXT_REGISTER import com.maddyhome.idea.vim.register.RegisterConstants.LAST_INSERTED_TEXT_REGISTER
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.toReturnTo import com.maddyhome.idea.vim.state.mode.toReturnTo
@ -172,7 +170,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
if (type == null || if (type == null ||
(mode == Mode.INSERT || mode == Mode.REPLACE) || (mode == Mode.INSERT || mode == Mode.REPLACE) ||
!saveToRegister || !saveToRegister ||
caret.registerStorage.storeText(editor, updatedRange, type, true) injector.registerGroup.storeText(editor, updatedRange, type, true, !editor.isFirstCaret, editor.isReversingCarets)
) { ) {
val startOffsets = updatedRange.startOffsets val startOffsets = updatedRange.startOffsets
val endOffsets = updatedRange.endOffsets val endOffsets = updatedRange.endOffsets
@ -201,7 +199,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
* @param caret The caret to start insertion in * @param caret The caret to start insertion in
* @param str The text to insert * @param str The text to insert
*/ */
override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: String): VimCaret { override fun insertText(editor: VimEditor, caret: VimCaret, offset: Int, str: CharSequence): VimCaret {
(editor as MutableVimEditor).insertText(caret, offset, str) (editor as MutableVimEditor).insertText(caret, offset, str)
val newCaret = caret.moveToInlayAwareOffset(offset + str.length) val newCaret = caret.moveToInlayAwareOffset(offset + str.length)
@ -430,7 +428,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
* @param mode The mode - indicate insert or replace * @param mode The mode - indicate insert or replace
*/ */
override fun initInsert(editor: VimEditor, context: ExecutionContext, mode: Mode) { override fun initInsert(editor: VimEditor, context: ExecutionContext, mode: Mode) {
val state = getInstance(editor) val state = injector.vimState
for (caret in editor.nativeCarets()) { for (caret in editor.nativeCarets()) {
caret.vimInsertStart = editor.createLiveMarker(caret.offset, caret.offset) caret.vimInsertStart = editor.createLiveMarker(caret.offset, caret.offset)
injector.markService.setMark(caret, MARK_CHANGE_START, caret.offset) injector.markService.setMark(caret, MARK_CHANGE_START, caret.offset)
@ -442,7 +440,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
editor.insertMode = false editor.insertMode = false
} }
if (cmd.flags.contains(CommandFlags.FLAG_NO_REPEAT_INSERT)) { if (cmd.flags.contains(CommandFlags.FLAG_NO_REPEAT_INSERT)) {
val commandState = getInstance(editor) val commandState = injector.vimState
repeatInsert( repeatInsert(
editor, editor,
context, context,
@ -451,7 +449,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
OperatorArguments(false, 1, commandState.mode), OperatorArguments(false, 1, commandState.mode),
) )
} else { } else {
val commandState = getInstance(editor) val commandState = injector.vimState
repeatInsert( repeatInsert(
editor, editor,
context, context,
@ -484,7 +482,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
} }
override fun runEnterAction(editor: VimEditor, context: ExecutionContext) { override fun runEnterAction(editor: VimEditor, context: ExecutionContext) {
val state = getInstance(editor) val state = injector.vimState
if (!state.isDotRepeatInProgress) { if (!state.isDotRepeatInProgress) {
// While repeating the enter action has been already executed because `initInsert` repeats the input // While repeating the enter action has been already executed because `initInsert` repeats the input
val action = injector.nativeActionManager.enterAction val action = injector.nativeActionManager.enterAction
@ -502,7 +500,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
} }
override fun runEnterAboveAction(editor: VimEditor, context: ExecutionContext) { override fun runEnterAboveAction(editor: VimEditor, context: ExecutionContext) {
val state = getInstance(editor) val state = injector.vimState
if (!state.isDotRepeatInProgress) { if (!state.isDotRepeatInProgress) {
// While repeating the enter action has been already executed because `initInsert` repeats the input // While repeating the enter action has been already executed because `initInsert` repeats the input
val action = injector.nativeActionManager.createLineAboveCaret val action = injector.nativeActionManager.createLineAboveCaret
@ -543,7 +541,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
val markGroup = injector.markService val markGroup = injector.markService
markGroup.setMark(editor, VimMarkService.INSERT_EXIT_MARK) markGroup.setMark(editor, VimMarkService.INSERT_EXIT_MARK)
markGroup.setMark(editor, MARK_CHANGE_END) markGroup.setMark(editor, MARK_CHANGE_END)
if (getInstance(editor).mode is Mode.REPLACE) { if (editor.mode is Mode.REPLACE) {
editor.insertMode = true editor.insertMode = true
} }
var cnt = if (lastInsert != null) lastInsert!!.count else 0 var cnt = if (lastInsert != null) lastInsert!!.count else 0
@ -558,7 +556,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
if (context != null) { if (context != null) {
injector.changeGroup.repeatInsert(editor, context, if (cnt == 0) 0 else cnt - 1, true, operatorArguments) injector.changeGroup.repeatInsert(editor, context, if (cnt == 0) 0 else cnt - 1, true, operatorArguments)
} }
if (getInstance(editor).mode is Mode.INSERT) { if (editor.mode is Mode.INSERT) {
updateLastInsertedTextRegister() updateLastInsertedTextRegister()
} }
@ -589,7 +587,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
* @param context The data context * @param context The data context
*/ */
override fun processEnter(editor: VimEditor, context: ExecutionContext) { override fun processEnter(editor: VimEditor, context: ExecutionContext) {
if (editor.vimStateMachine.mode is Mode.REPLACE) { if (editor.mode is Mode.REPLACE) {
editor.insertMode = true editor.insertMode = true
} }
val enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0) val enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
@ -599,7 +597,7 @@ abstract class VimChangeGroupBase : VimChangeGroup {
break break
} }
} }
if (editor.vimStateMachine.mode is Mode.REPLACE) { if (editor.mode is Mode.REPLACE) {
editor.insertMode = false editor.insertMode = false
} }
} }

View File

@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.LiveRange import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnTo import com.maddyhome.idea.vim.state.mode.ReturnTo
@ -126,20 +125,22 @@ import com.maddyhome.idea.vim.state.mode.returnTo
*/ */
interface VimEditor { interface VimEditor {
var mode: Mode var mode: Mode
get() = vimStateMachine.mode get() = injector.vimState.mode
set(value) { set(value) {
if (vimStateMachine.mode == value) return val vimState = injector.vimState
if (vimState.mode == value) return
val oldValue = vimStateMachine.mode val oldValue = vimState.mode
(vimStateMachine as VimStateMachineImpl).mode = value (vimState as VimStateMachineImpl).mode = value
injector.listenersNotifier.notifyModeChanged(this, oldValue) injector.listenersNotifier.notifyModeChanged(this, oldValue)
} }
var isReplaceCharacter: Boolean var isReplaceCharacter: Boolean
get() = vimStateMachine.isReplaceCharacter get() = injector.vimState.isReplaceCharacter
set(value) { set(value) {
if (value != vimStateMachine.isReplaceCharacter) { val vimState = injector.vimState
(vimStateMachine as VimStateMachineImpl).isReplaceCharacter = value if (value != vimState.isReplaceCharacter) {
(vimState as VimStateMachineImpl).isReplaceCharacter = value
injector.listenersNotifier.notifyIsReplaceCharChanged(this) injector.listenersNotifier.notifyIsReplaceCharChanged(this)
} }
} }
@ -162,7 +163,7 @@ interface VimEditor {
* which indicated that the buffer is empty. However, the line count is still 1. * which indicated that the buffer is empty. However, the line count is still 1.
* *
* The variable for line count is named `ml_line_count` in `memline` structure. There is a single spot where * The variable for line count is named `ml_line_count` in `memline` structure. There is a single spot where
* `0` is assigned to this variable (at the end of `buf_freeall` public function), however I'm not sure that this affects * `0` is assigned to this variable (at the end of `buf_freeall` function), however I'm not sure that this affects
* the opened buffer. * the opened buffer.
* Another thing that I don't understand is that I don't see where this variable is updated. There is a small chance * Another thing that I don't understand is that I don't see where this variable is updated. There is a small chance
* that this variable doesn't present the line count, so I may be wrong and line count can return zero. * that this variable doesn't present the line count, so I may be wrong and line count can return zero.
@ -188,7 +189,8 @@ interface VimEditor {
* This method should perform caret merging after the operations. This is similar to IJ runForEachCaret * This method should perform caret merging after the operations. This is similar to IJ runForEachCaret
* TODO review * TODO review
*/ */
val isFirstCaret: Boolean
val isReversingCarets: Boolean
fun forEachCaret(action: (VimCaret) -> Unit) fun forEachCaret(action: (VimCaret) -> Unit)
fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false) fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false)
fun isInForEachCaretScope(): Boolean fun isInForEachCaretScope(): Boolean
@ -207,7 +209,7 @@ interface VimEditor {
fun isOneLineMode(): Boolean fun isOneLineMode(): Boolean
/** /**
* public function for refactoring, get rid of it * function for refactoring, get rid of it
*/ */
fun search( fun search(
pair: Pair<Int, Int>, pair: Pair<Int, Int>,
@ -303,6 +305,23 @@ interface VimEditor {
} }
} }
} }
/**
* Toggles the insert/overwrite state. If currently insert, goto replace mode. If currently replace, goto insert
* mode.
*/
fun toggleInsertOverwrite() {
val oldMode = this.mode
var newMode = oldMode
if (oldMode == Mode.INSERT) {
newMode = Mode.REPLACE
} else if (oldMode == Mode.REPLACE) {
newMode = Mode.INSERT
}
if (oldMode != newMode) {
mode = newMode
}
}
} }
interface MutableVimEditor : VimEditor { interface MutableVimEditor : VimEditor {

View File

@ -1,23 +0,0 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimExOutputPanelService {
fun getPanel(editor: VimEditor): VimExOutputPanel
}
interface VimExOutputPanel {
val isActive: Boolean
val text: String?
fun output(text: String)
fun clear()
fun close()
}

View File

@ -22,6 +22,8 @@ import com.maddyhome.idea.vim.vimscript.services.VariableService
import com.maddyhome.idea.vim.yank.VimYankGroup import com.maddyhome.idea.vim.yank.VimYankGroup
interface VimInjector { interface VimInjector {
val vimState: VimStateMachine
/** /**
* The window used when we need a window but there are no editor windows available. * The window used when we need a window but there are no editor windows available.
* *
@ -85,12 +87,14 @@ interface VimInjector {
val visualMotionGroup: VimVisualMotionGroup val visualMotionGroup: VimVisualMotionGroup
// [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things // [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
fun commandStateFor(editor: VimEditor): VimStateMachine fun commandStateFor(editor: VimEditor): VimStateMachine
// [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things // [FINISHED] Class moved to vim-engine, but it's attached to Editor using IJ things
/** /**
* COMPATIBILITY-LAYER: Added new method with Any * COMPATIBILITY-LAYER: Added new method with Any
* Please see: https://jb.gg/zo8n0r * Please see: https://jb.gg/zo8n0r
*/ */
@Deprecated("Please use VimInjector.vimState", replaceWith = ReplaceWith("vimState"))
fun commandStateFor(editor: Any): VimStateMachine fun commandStateFor(editor: Any): VimStateMachine
// !! in progress // !! in progress
@ -108,9 +112,6 @@ interface VimInjector {
// Can't be fully moved to vim-engine. // Can't be fully moved to vim-engine.
val actionExecutor: VimActionExecutor val actionExecutor: VimActionExecutor
// Can't be fully moved to vim-engine.
val exOutputPanel: VimExOutputPanelService
// Can't be fully moved to vim-engine. // Can't be fully moved to vim-engine.
val clipboardManager: VimClipboardManager val clipboardManager: VimClipboardManager
@ -175,6 +176,7 @@ interface VimInjector {
val variableService: VariableService val variableService: VariableService
val commandLine: VimCommandLineService val commandLine: VimCommandLineService
val outputPanel: VimOutputPanelService
// !! in progress // !! in progress
val functionService: VimscriptFunctionService val functionService: VimscriptFunctionService

View File

@ -16,8 +16,10 @@ import com.maddyhome.idea.vim.api.stubs.VimProcessGroupStub
import com.maddyhome.idea.vim.common.VimListenersNotifier import com.maddyhome.idea.vim.common.VimListenersNotifier
import com.maddyhome.idea.vim.diagnostic.VimLogger import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.register.VimRegisterGroup import com.maddyhome.idea.vim.register.VimRegisterGroup
import com.maddyhome.idea.vim.register.VimRegisterGroupBase import com.maddyhome.idea.vim.register.VimRegisterGroupBase
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.vimscript.services.VariableService import com.maddyhome.idea.vim.vimscript.services.VariableService
import com.maddyhome.idea.vim.vimscript.services.VimVariableServiceBase import com.maddyhome.idea.vim.vimscript.services.VimVariableServiceBase
import com.maddyhome.idea.vim.yank.VimYankGroup import com.maddyhome.idea.vim.yank.VimYankGroup
@ -29,6 +31,8 @@ abstract class VimInjectorBase : VimInjector {
val registerGroupStub: VimRegisterGroupBase by lazy { object : VimRegisterGroupBase() {} } val registerGroupStub: VimRegisterGroupBase by lazy { object : VimRegisterGroupBase() {} }
} }
override val vimState: VimStateMachine = VimStateMachineImpl()
override val parser: VimStringParser = object : VimStringParserBase() {} override val parser: VimStringParser = object : VimStringParserBase() {}
override val optionGroup: VimOptionGroup by lazy { object : VimOptionGroupBase() {} } override val optionGroup: VimOptionGroup by lazy { object : VimOptionGroupBase() {} }

View File

@ -27,7 +27,6 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.diagnostic.debug import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.mark.Jump import com.maddyhome.idea.vim.mark.Jump
import com.maddyhome.idea.vim.mark.Mark import com.maddyhome.idea.vim.mark.Mark
import com.maddyhome.idea.vim.mark.VimMark import com.maddyhome.idea.vim.mark.VimMark
@ -368,7 +367,7 @@ abstract class VimMarkServiceBase : VimMarkService {
val markLineStartOffset = editor.getLineStartOffset(mark.line) val markLineStartOffset = editor.getLineStartOffset(mark.line)
val markLineEndOffset = editor.getLineEndOffset(mark.line, true) val markLineEndOffset = editor.getLineEndOffset(mark.line, true)
val command = editor.vimStateMachine.executingCommand val command = injector.vimState.executingCommand
// If text is being changed from the start of the mark line (a special case for mark deletion) // If text is being changed from the start of the mark line (a special case for mark deletion)
val changeFromMarkLineStart = val changeFromMarkLineStart =
(command != null && command.type === Command.Type.CHANGE && delStartOffset == markLineStartOffset) (command != null && command.type === Command.Type.CHANGE && delStartOffset == markLineStartOffset)

View File

@ -0,0 +1,39 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimOutputPanel {
/**
* The current text displayed in the output panel.
* The actual text may be different (if we called the [addText] method and did not call [show] afterward)
*/
val text: String
/**
* Appends the specified text to the existing content of the output panel.
* If 'isNewLine' is true, the text will begin on a new line.
*
* Note: The full text content is not updated in the display until [show] is invoked.
*
* @param text The text to append.
* @param isNewLine Whether to start the appended text on a new line. Defaults to true.
*/
fun addText(text: String, isNewLine: Boolean = true)
/**
* This method shows the text output or updates the output text if the panel was already shown
*/
fun show()
/**
* Disposes or hides the output panel, depending on its implementation.
* This may free any associated resources.
*/
fun close()
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
interface VimOutputPanelService {
/**
* Creates a new VimOutputPanel instance for building output without affecting the current panel until displayed.
*/
// TODO make it possible to pass null instead of editor
fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel
/**
* Retrieves the current VimOutputPanel or creates a new one if none exists.
*/
fun getOrCreate(editor: VimEditor, context: ExecutionContext): VimOutputPanel
/**
* Returns the currently active VimOutputPanel, if available.
*/
fun getCurrentOutputPanel(): VimOutputPanel?
/**
* Appends text to the existing output panel or creates a new one with the given text.
* Basic method that should be sufficient in most cases.
*/
fun output(editor: VimEditor, context: ExecutionContext, text: String)
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2003-2024 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package com.maddyhome.idea.vim.api
abstract class VimOutputPanelServiceBase : VimOutputPanelService {
override fun getOrCreate(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
return getCurrentOutputPanel() ?: create(editor, context)
}
override fun output(editor: VimEditor, context: ExecutionContext, text: String) {
val panel = getOrCreate(editor, context)
panel.addText(text)
panel.show()
}
}

View File

@ -21,7 +21,7 @@ interface VimProcessGroup {
// TODO remove me // TODO remove me
// TODO: Why ^^ ? Should that also include startExEntry? // TODO: Why ^^ ? Should that also include startExEntry?
fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean
fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) fun cancelExEntry(editor: VimEditor, refocusOwningEditor: Boolean, resetCaret: Boolean)
@kotlin.jvm.Throws(java.lang.Exception::class) @kotlin.jvm.Throws(java.lang.Exception::class)
fun executeCommand(editor: VimEditor, command: String, input: CharSequence?, currentDirectoryPath: String?): String? fun executeCommand(editor: VimEditor, command: String, input: CharSequence?, currentDirectoryPath: String?): String?

View File

@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
import com.maddyhome.idea.vim.KeyProcessResult import com.maddyhome.idea.vim.KeyProcessResult
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
import com.maddyhome.idea.vim.state.mode.inVisualMode import com.maddyhome.idea.vim.state.mode.inVisualMode
@ -27,7 +26,7 @@ abstract class VimProcessGroupBase : VimProcessGroup {
// Don't allow ex commands in one line editors // Don't allow ex commands in one line editors
if (editor.isOneLineMode()) return if (editor.isOneLineMode()) return
val currentMode = editor.vimStateMachine.mode val currentMode = editor.mode
check(currentMode is ReturnableFromCmd) { check(currentMode is ReturnableFromCmd) {
"Cannot enable cmd mode from current mode $currentMode" "Cannot enable cmd mode from current mode $currentMode"
} }
@ -76,12 +75,12 @@ abstract class VimProcessGroupBase : VimProcessGroup {
} }
} }
override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) { override fun cancelExEntry(editor: VimEditor, refocusOwningEditor: Boolean, resetCaret: Boolean) {
// If 'cpoptions' contains 'x', then Escape should execute the command line. This is the default for Vi but not Vim. // If 'cpoptions' contains 'x', then Escape should execute the command line. This is the default for Vi but not Vim.
// IdeaVim does not (currently?) support 'cpoptions', so sticks with Vim's default behaviour. Escape cancels. // IdeaVim does not (currently?) support 'cpoptions', so sticks with Vim's default behaviour. Escape cancels.
editor.mode = editor.mode.returnTo() editor.mode = editor.mode.returnTo()
getInstance().reset(editor) getInstance().reset(editor)
injector.commandLine.getActiveCommandLine()?.deactivate(refocusOwningEditor = true, resetCaret) injector.commandLine.getActiveCommandLine()?.deactivate(refocusOwningEditor, resetCaret)
} }
private fun getRange(editor: VimEditor, cmd: Command) = when { private fun getRange(editor: VimEditor, cmd: Command) = when {

View File

@ -25,7 +25,6 @@ interface VimSearchGroup {
* Last used pattern to perform a substitution. * Last used pattern to perform a substitution.
*/ */
var lastSubstitutePattern: String? var lastSubstitutePattern: String?
fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange? fun searchBackward(editor: VimEditor, offset: Int, count: Int): TextRange?
/** /**
@ -195,4 +194,17 @@ interface VimSearchGroup {
* Gets the direction lastly used in a search. * Gets the direction lastly used in a search.
*/ */
fun getLastSearchDirection(): Direction fun getLastSearchDirection(): Direction
/**
* Sets the last search state purely for tests
*
* @param pattern The pattern to save. This is the last search pattern, not the last substitute pattern
* @param patternOffset The pattern offset, e.g. `/{pattern}/{offset}`
* @param direction The direction to search
*/
fun setLastSearchState(
pattern: String,
patternOffset: String,
direction: Direction,
)
} }

View File

@ -1188,8 +1188,7 @@ abstract class VimSearchGroupBase : VimSearchGroup {
* @param patternOffset The pattern offset, e.g. `/{pattern}/{offset}` * @param patternOffset The pattern offset, e.g. `/{pattern}/{offset}`
* @param direction The direction to search * @param direction The direction to search
*/ */
@TestOnly override fun setLastSearchState(
fun setLastSearchState(
pattern: String, pattern: String,
patternOffset: String, patternOffset: String,
direction: Direction, direction: Direction,

View File

@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.regexp.VimRegex
import com.maddyhome.idea.vim.regexp.VimRegexException import com.maddyhome.idea.vim.regexp.VimRegexException
import com.maddyhome.idea.vim.regexp.VimRegexOptions import com.maddyhome.idea.vim.regexp.VimRegexOptions
import com.maddyhome.idea.vim.regexp.match.VimMatchResult import com.maddyhome.idea.vim.regexp.match.VimMatchResult
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import org.jetbrains.annotations.Range import org.jetbrains.annotations.Range
@ -1454,7 +1453,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') { while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') {
selectionEndWithoutNewline++ selectionEndWithoutNewline++
} }
val mode = VimStateMachine.getInstance(editor).mode val mode = editor.mode
if (mode is Mode.VISUAL) { if (mode is Mode.VISUAL) {
if (closingTagTextRange.startOffset == selectionEndWithoutNewline && if (closingTagTextRange.startOffset == selectionEndWithoutNewline &&
openingTag.endOffset == selectionStart openingTag.endOffset == selectionStart

Some files were not shown because too many files have changed in this diff Show More