1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2024-11-25 16:42:55 +01:00

Compare commits

..

15 Commits

Author SHA1 Message Date
fd1a706e4a
Set plugin version to chylex-22 2023-12-03 09:31:23 +01:00
5b2f3e1f12
Automatically add unambiguous imports after running a macro 2023-12-03 09:31:23 +01:00
ec704fc9f9
Prevent code completion popup from appearing after running a macro 2023-12-03 07:27:33 +01:00
ef8955e9d1
Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2023-12-03 04:12:13 +01:00
806f6f8eaa
Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2023-12-03 04:12:12 +01:00
6dcf0f9458
Fix(VIM-3166): Workaround to fix broken filtering of visual lines 2023-12-03 04:12:12 +01:00
8eb201a941
Add support for count for visual and line motion surround 2023-12-03 04:12:12 +01:00
a288feca2a
Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2023-12-03 04:12:12 +01:00
ffe337b145
[VIM-696] Restore visual mode after undo/redo, and disable incompatible actions 2023-12-03 04:12:12 +01:00
ee178e58d0
Respect count with <Action> mappings 2023-12-03 04:12:12 +01:00
12ac424ae0
Prevent IdeaVIM from stealing key binding that confirms in-place refactoring 2023-12-03 04:09:40 +01:00
c96ca2e405
Change matchit plugin to use HTML patterns in unrecognized files 2023-12-03 04:09:40 +01:00
0265432ce5
Reset insert mode when switching active editor 2023-12-03 04:09:39 +01:00
f7e5f15ed2
Remove update checker 2023-12-03 04:09:39 +01:00
68f21ad7c9
Set custom plugin version 2023-12-03 04:09:39 +01:00
145 changed files with 1137 additions and 3083 deletions

View File

@ -8,20 +8,18 @@ jobs:
if: github.repository == 'JetBrains/ideavim' if: github.repository == 'JetBrains/ideavim'
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v2.1.0
with: with:
distribution: zulu distribution: zulu
java-version: 11 java-version: 11
- name: Setup FFmpeg - name: Setup FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v3 uses: FedericoCarboni/setup-ffmpeg@v1
with: with:
# Not strictly necessary, but it may prevent rate limit # Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines. # errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin - name: Build Plugin
run: gradle :buildPlugin run: gradle :buildPlugin
- name: Run Idea - name: Run Idea
@ -29,7 +27,7 @@ jobs:
mkdir -p build/reports mkdir -p build/reports
gradle :runIdeForUiTests > 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@1.5
with: with:
url: http://127.0.0.1:8082 url: http://127.0.0.1:8082
max-attempts: 20 max-attempts: 20
@ -37,19 +35,15 @@ jobs:
- name: Tests - name: Tests
run: gradle :testUi run: gradle :testUi
- name: Move video - name: Move video
if: always() if: ${{ failure() }}
run: mv video build/reports run: mv video build/reports
- name: Move sandbox logs - name: Save fails report
if: always() if: ${{ failure() }}
run: mv build/idea-sandbox/system/log sandbox-idea-log uses: actions/upload-artifact@v2
- name: Save report
if: always()
uses: actions/upload-artifact@v4
with: with:
name: ui-test-fails-report-mac name: ui-test-fails-report-mac
path: | path: |
build/reports build/reports
sandbox-idea-log
# build-for-ui-test-linux: # build-for-ui-test-linux:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
# steps: # steps:

View File

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="Copyright 2003-&amp;#36;today.year The IdeaVim authors&#10;&#10;Use of this source code is governed by an MIT-style&#10;license that can be found in the LICENSE.txt file or at&#10;https://opensource.org/licenses/MIT." /> <option name="notice" value="Copyright 2003-2023 The IdeaVim authors&#10;&#10;Use of this source code is governed by an MIT-style&#10;license that can be found in the LICENSE.txt file or at&#10;https://opensource.org/licenses/MIT." />
<option name="myName" value="IdeaVim" /> <option name="myName" value="IdeaVim" />
</copyright> </copyright>
</component> </component>

View File

@ -5,13 +5,14 @@ object Constants {
const val EAP_CHANNEL = "eap" const val EAP_CHANNEL = "eap"
const val DEV_CHANNEL = "Dev" const val DEV_CHANNEL = "Dev"
const val GITHUB_TESTS = "2023.3.2" // TODO it should be 2023.3 as soon as it releases
const val NVIM_TESTS = "2023.3.2" const val GITHUB_TESTS = "LATEST-EAP-SNAPSHOT"
const val PROPERTY_TESTS = "2023.3.2" const val NVIM_TESTS = "LATEST-EAP-SNAPSHOT"
const val LONG_RUNNING_TESTS = "2023.3.2" const val PROPERTY_TESTS = "LATEST-EAP-SNAPSHOT"
const val QODANA_TESTS = "2023.3.2" const val LONG_RUNNING_TESTS = "LATEST-EAP-SNAPSHOT"
const val RELEASE = "2023.3.2" const val QODANA_TESTS = "LATEST-EAP-SNAPSHOT"
const val RELEASE = "LATEST-EAP-SNAPSHOT"
const val RELEASE_DEV = "2023.3.2" const val RELEASE_DEV = "LATEST-EAP-SNAPSHOT"
const val RELEASE_EAP = "2023.3.2" const val RELEASE_EAP = "LATEST-EAP-SNAPSHOT"
} }

View File

@ -24,7 +24,6 @@ object Project : Project({
// Active tests // Active tests
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT")) buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
buildType(PropertyBased) buildType(PropertyBased)
@ -39,11 +38,6 @@ object Project : Project({
// Common build type for all configurations // Common build type for all configurations
abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({ abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
artifactRules = """
+:build/reports => build/reports
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
""".trimIndent()
init() init()
requirements { requirements {

View File

@ -1,29 +0,0 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
expectSteps {
gradle {
tasks = "clean test"
buildFile = ""
enableStacktrace = true
}
}
steps {
update<GradleBuildStep>(0) {
clearConditions()
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
}
}
}

View File

@ -1,17 +0,0 @@
package patches.projects
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the root project
accordingly, and delete the patch script.
*/
changeProject(DslContext.projectId) {
check(description == "Vim engine for IDEs based on the IntelliJ platform") {
"Unexpected description: '$description'"
}
description = "Vim engine for JetBrains IDEs"
}

View File

@ -487,10 +487,6 @@ Contributors:
[![icon][github]](https://github.com/pWydmuch) [![icon][github]](https://github.com/pWydmuch)
&nbsp; &nbsp;
pWydmuch pWydmuch
* [![icon][mail]](mailto:leonid989@gmail.com)
[![icon][github]](https://github.com/Infonautica)
&nbsp;
Leonid Danilov
Previous contributors: Previous contributors:

View File

@ -43,14 +43,9 @@ usual beta standards.
* [VIM-3176](https://youtrack.jetbrains.com/issue/VIM-3176) Reselecting visual selection after pasting above it select wrong lines * [VIM-3176](https://youtrack.jetbrains.com/issue/VIM-3176) Reselecting visual selection after pasting above it select wrong lines
* [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape * [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape
* [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode * [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode
* [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction
### Merged PRs: ### Merged PRs:
* [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s… * [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s…
* [772](https://github.com/JetBrains/ideavim/pull/772) by [chylex](https://github.com/chylex): Prevent code completion popup from appearing after running a macro
* [787](https://github.com/JetBrains/ideavim/pull/787) by [Leonid Danilov](https://github.com/Infonautica): Added "Which-Key" to Plugins
* [778](https://github.com/JetBrains/ideavim/pull/778) by [lippfi](https://github.com/lippfi): Showmode
* [788](https://github.com/JetBrains/ideavim/pull/788) by [Matt Ellis](https://github.com/citizenmatt): Refactor VimOptionGroupBase
## 2.7.0, 2023-11-07 ## 2.7.0, 2023-11-07

View File

@ -21,7 +21,7 @@ repositories {
} }
dependencies { dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.16") compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.21-1.0.15")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution // kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
exclude("org.jetbrains.kotlin", "kotlin-stdlib") exclude("org.jetbrains.kotlin", "kotlin-stdlib")

View File

@ -49,14 +49,14 @@ buildscript {
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh // This is needed for jgit to connect to ssh
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r") classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.7.0.202309050840-r")
classpath("org.kohsuke:github-api:1.305") classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:2.3.7") classpath("io.ktor:ktor-client-core:2.3.6")
classpath("io.ktor:ktor-client-cio:2.3.7") classpath("io.ktor:ktor-client-cio:2.3.6")
classpath("io.ktor:ktor-client-auth:2.3.7") classpath("io.ktor:ktor-client-auth:2.3.6")
classpath("io.ktor:ktor-client-content-negotiation:2.3.7") classpath("io.ktor:ktor-client-content-negotiation:2.3.6")
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7") classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.6")
// This comes from the changelog plugin // This comes from the changelog plugin
// classpath("org.jetbrains:markdown:0.3.1") // classpath("org.jetbrains:markdown:0.3.1")
@ -69,7 +69,7 @@ plugins {
kotlin("jvm") version "1.8.21" kotlin("jvm") version "1.8.21"
application application
id("org.jetbrains.intellij") version "1.16.1" id("org.jetbrains.intellij") version "1.16.0"
id("org.jetbrains.changelog") version "2.2.0" id("org.jetbrains.changelog") version "2.2.0"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle // ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
@ -126,11 +126,11 @@ dependencies {
testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin // https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1") testImplementation("org.mockito.kotlin:mockito-kotlin:5.1.0")
testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion")
testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion")
testImplementation("com.automation-remarks:video-recorder-junit5:2.0") testImplementation("com.automation-remarks:video-recorder-junit:2.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion") runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
antlr("org.antlr:antlr4:$antlrVersion") antlr("org.antlr:antlr4:$antlrVersion")
@ -184,14 +184,6 @@ tasks {
include("**/*test.class") include("**/*test.class")
include("**/*Tests.class") include("**/*Tests.class")
exclude("**/ParserTest.class") exclude("**/ParserTest.class")
// Set teamcity env variable locally to run additional tests for leaks.
// By default, this test runs on TC only, but this test doesn't take a lot of time,
// so we can turn it on for local development
if (environment["TEAMCITY_VERSION"] == null) {
println("Set env TEAMCITY_VERSION to X")
environment("TEAMCITY_VERSION" to "X")
}
} }
val testWithNeovim by getting(Test::class) { val testWithNeovim by getting(Test::class) {
@ -302,7 +294,6 @@ tasks {
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-->")
systemProperty("jb.consents.confirmation.enabled", "false") systemProperty("jb.consents.confirmation.enabled", "false")
systemProperty("ide.show.tips.on.startup.default.value", "false")
} }
runPluginVerifier { runPluginVerifier {

View File

@ -396,19 +396,3 @@ Original plugin: [quick-scope](https://github.com/unblevable/quick-scope).
https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
</details> </details>
<details>
<summary><h2>Which-Key</h2></summary>
Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
### Setup:
- Install [Which-Key](https://plugins.jetbrains.com/plugin/15976-which-key) plugin.
- Add the following command to `~/.ideavimrc`: `set which-key`
### Instructions
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
</details>

View File

@ -8,12 +8,12 @@
# suppress inspection "UnusedProperty" for whole file # suppress inspection "UnusedProperty" for whole file
ideaVersion=2023.3.2 ideaVersion=2023.2
downloadIdeaSources=true downloadIdeaSources=true
instrumentPluginCode=true instrumentPluginCode=true
version=chylex-23 version=chylex-22
javaVersion=17 javaVersion=17
remoteRobotVersion=0.11.21 remoteRobotVersion=0.11.17
antlrVersion=4.10.1 antlrVersion=4.10.1
kotlin.incremental.useClasspathSnapshot=false kotlin.incremental.useClasspathSnapshot=false

View File

@ -20,17 +20,17 @@ repositories {
} }
dependencies { dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.21")
implementation("io.ktor:ktor-client-core:2.3.7") implementation("io.ktor:ktor-client-core:2.3.6")
implementation("io.ktor:ktor-client-cio:2.3.7") implementation("io.ktor:ktor-client-cio:2.3.6")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7") implementation("io.ktor:ktor-client-content-negotiation:2.3.6")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7") implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.6")
implementation("io.ktor:ktor-client-auth:2.3.7") implementation("io.ktor:ktor-client-auth:2.3.6")
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r") implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
// This is needed for jgit to connect to ssh // This is needed for jgit to connect to ssh
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r") implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.7.0.202309050840-r")
implementation("com.vdurmont:semver4j:3.1.0") implementation("com.vdurmont:semver4j:3.1.0")
} }

View File

@ -219,10 +219,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return getInstance().enabled; return getInstance().enabled;
} }
public static boolean isNotEnabled() {
return !isEnabled();
}
public static void setEnabled(final boolean enabled) { public static void setEnabled(final boolean enabled) {
if (isEnabled() == enabled) return; if (isEnabled() == enabled) return;
@ -236,12 +232,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
getInstance().turnOnPlugin(); getInstance().turnOnPlugin();
} }
if (enabled) {
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
} else {
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
}
StatusBarIconFactory.Util.INSTANCE.updateIcon(); StatusBarIconFactory.Util.INSTANCE.updateIcon();
} }

View File

@ -28,11 +28,8 @@ import javax.swing.KeyStroke
* Accepts all regular keystrokes and passes them on to the Vim key handler. * Accepts all regular keystrokes and passes them on to the Vim key handler.
* *
* IDE shortcut keys used by Vim commands are handled by [com.maddyhome.idea.vim.action.VimShortcutKeyAction]. * IDE shortcut keys used by Vim commands are handled by [com.maddyhome.idea.vim.action.VimShortcutKeyAction].
*
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
* way to get ideavim keys for this plugin. See VIM-3085
*/ */
public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx { internal class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
private val handler = KeyHandler.getInstance() private val handler = KeyHandler.getInstance()
private val traceTime = injector.globalOptions().ideatracetime private val traceTime = injector.globalOptions().ideatracetime
@ -89,7 +86,7 @@ public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActio
} }
} }
internal companion object { companion object {
private val LOG = logger<VimTypedActionHandler>() private val LOG = logger<VimTypedActionHandler>()
} }
} }

View File

@ -14,10 +14,14 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.AnActionWrapper import com.intellij.openapi.actionSystem.AnActionWrapper
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.application.invokeLater import com.intellij.openapi.application.invokeLater
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorActionManager
import com.intellij.openapi.keymap.KeymapManager
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
@ -54,11 +58,8 @@ import javax.swing.KeyStroke
* *
* *
* These keys are not passed to [com.maddyhome.idea.vim.VimTypedActionHandler] and should be handled by actions. * These keys are not passed to [com.maddyhome.idea.vim.VimTypedActionHandler] and should be handled by actions.
*
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
* way to get ideavim keys for this plugin. See VIM-3085
*/ */
public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ { internal class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
private val traceTime: Boolean private val traceTime: Boolean
get() { get() {
// Make sure the injector is initialized // Make sure the injector is initialized
@ -98,7 +99,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
// There is a chance that we can use BGT, but we call for isCell inside the update. // There is a chance that we can use BGT, but we call for isCell inside the update.
// Not sure if can can use BGT with this call. Let's use EDT for now. // Not sure if can can use BGT with this call. Let's use EDT for now.
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT override fun getActionUpdateThread() = ActionUpdateThread.EDT
override fun update(e: AnActionEvent) { override fun update(e: AnActionEvent) {
val start = if (traceTime) System.currentTimeMillis() else null val start = if (traceTime) System.currentTimeMillis() else null
@ -113,7 +114,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
} }
private fun isEnabled(e: AnActionEvent, keyStroke: KeyStroke?): ActionEnableStatus { private fun isEnabled(e: AnActionEvent, keyStroke: KeyStroke?): ActionEnableStatus {
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG) if (!VimPlugin.isEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
val editor = getEditor(e) val editor = getEditor(e)
if (editor != null && keyStroke != null) { if (editor != null && keyStroke != null) {
if (isOctopusEnabled(keyStroke, editor)) { if (isOctopusEnabled(keyStroke, editor)) {
@ -167,6 +168,14 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
return ActionEnableStatus.no("App code template is active", LogLevel.INFO) return ActionEnableStatus.no("App code template is active", LogLevel.INFO)
} }
val nextTemplateVariableShortcuts = KeymapManager.getInstance().activeKeymap.getShortcuts(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE)
if (nextTemplateVariableShortcuts.any { it is KeyboardShortcut && it.firstKeyStroke == keyStroke }) {
val handler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_NEXT_TEMPLATE_VARIABLE)
if (handler.isEnabled(editor, null, e.dataContext)) {
return ActionEnableStatus.no("Next template variable or finish in-place refactoring", LogLevel.INFO)
}
}
if (editor.inInsertMode) { if (editor.inInsertMode) {
if (keyCode == KeyEvent.VK_TAB) { if (keyCode == KeyEvent.VK_TAB) {
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view // TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
@ -232,9 +241,9 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
/** /**
* getDefaultKeyStroke is needed for NEO layout keyboard VIM-987 * getDefaultKeyStroke is needed for NEO layout keyboard VIM-987
* but we should cache the value because on the second call (isEnabled -> actionPerformed) * but we should cache the value because on the second call (isEnabled -> actionPerformed)
* the event is already consumed and getDefaultKeyStroke returns null * the event is already consumed
*/ */
private var keyStrokeCache: Pair<Long?, KeyStroke?> = null to null private var keyStrokeCache: Pair<KeyEvent?, KeyStroke?> = null to null
private fun getKeyStroke(e: AnActionEvent): KeyStroke? { private fun getKeyStroke(e: AnActionEvent): KeyStroke? {
val inputEvent = e.inputEvent val inputEvent = e.inputEvent
@ -242,9 +251,9 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
val defaultKeyStroke = KeyStrokeAdapter.getDefaultKeyStroke(inputEvent) val defaultKeyStroke = KeyStrokeAdapter.getDefaultKeyStroke(inputEvent)
val strokeCache = keyStrokeCache val strokeCache = keyStrokeCache
if (defaultKeyStroke != null) { if (defaultKeyStroke != null) {
keyStrokeCache = inputEvent.`when` to defaultKeyStroke keyStrokeCache = inputEvent to defaultKeyStroke
return defaultKeyStroke return defaultKeyStroke
} else if (strokeCache.first == inputEvent.`when`) { } else if (strokeCache.first === inputEvent) {
keyStrokeCache = null to null keyStrokeCache = null to null
return strokeCache.second return strokeCache.second
} }
@ -277,7 +286,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
.toSet() .toSet()
} }
internal companion object { companion object {
@JvmField @JvmField
val VIM_ONLY_EDITOR_KEYS: Set<KeyStroke> = val VIM_ONLY_EDITOR_KEYS: Set<KeyStroke> =
ImmutableSet.builder<KeyStroke>().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)) ImmutableSet.builder<KeyStroke>().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0))

View File

@ -18,7 +18,9 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
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.state.mode.SelectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.argumentCaptured import com.maddyhome.idea.vim.common.argumentCaptured
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
@ -26,9 +28,10 @@ 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.enumSetOf
import com.maddyhome.idea.vim.helper.vimStateMachine 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.state.mode.SelectionType import java.util.*
// todo make it multicaret // todo make it multicaret
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean { private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
@ -101,6 +104,8 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
internal class VisualOperatorAction : VisualOperatorActionHandler.ForEachCaret() { internal class VisualOperatorAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -14,10 +14,13 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.newapi.ijOptions import com.maddyhome.idea.vim.newapi.ijOptions
import java.util.*
/** /**
* @author vlan * @author vlan
@ -26,6 +29,8 @@ import com.maddyhome.idea.vim.newapi.ijOptions
public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() { public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeForAllCarets( override fun executeForAllCarets(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,

View File

@ -14,10 +14,13 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.newapi.ijOptions import com.maddyhome.idea.vim.newapi.ijOptions
import java.util.*
/** /**
* @author vlan * @author vlan
@ -26,6 +29,8 @@ import com.maddyhome.idea.vim.newapi.ijOptions
public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() { public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeForAllCarets( override fun executeForAllCarets(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,

View File

@ -9,7 +9,6 @@ package com.maddyhome.idea.vim.extension
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
@ -18,6 +17,7 @@ import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.common.CommandAlias 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.helper.CommandLineHelper import com.maddyhome.idea.vim.helper.CommandLineHelper
@ -26,7 +26,6 @@ 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
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.ui.ModalEntry import com.maddyhome.idea.vim.ui.ModalEntry
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.KeyStroke import javax.swing.KeyStroke
@ -39,9 +38,6 @@ import javax.swing.KeyStroke
* @author vlan * @author vlan
*/ */
public object VimExtensionFacade { public object VimExtensionFacade {
private val LOG = logger<VimExtensionFacade>()
/** The 'map' command for mapping keys to handlers defined in extensions. */ /** The 'map' command for mapping keys to handlers defined in extensions. */
@JvmStatic @JvmStatic
public fun putExtensionHandlerMapping( public fun putExtensionHandlerMapping(
@ -144,12 +140,10 @@ public object VimExtensionFacade {
public fun inputKeyStroke(editor: Editor): KeyStroke { public fun inputKeyStroke(editor: Editor): KeyStroke {
if (editor.vim.vimStateMachine.isDotRepeatInProgress) { if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
val input = Extension.consumeKeystroke() val input = Extension.consumeKeystroke()
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}")
} }
val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) { val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) {
LOG.trace("Unit test mode is active")
val mappingStack = KeyHandler.getInstance().keyStack val mappingStack = KeyHandler.getInstance().keyStack
mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also { mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
if (editor.vim.vimStateMachine.isRecording) { if (editor.vim.vimStateMachine.isRecording) {
@ -157,13 +151,11 @@ public object VimExtensionFacade {
} }
} }
} else { } else {
LOG.trace("Getting char from the modal entry...")
var ref: KeyStroke? = null var ref: KeyStroke? = null
ModalEntry.activate(editor.vim) { stroke: KeyStroke? -> ModalEntry.activate(editor.vim) { stroke: KeyStroke? ->
ref = stroke ref = stroke
false false
} }
LOG.trace("Got char $ref")
ref ref
} }
val result = key ?: KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE.toChar()) val result = key ?: KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE.toChar())

View File

@ -156,6 +156,11 @@ internal class CommentaryExtension : VimExtension {
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler { private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
override val isRepeatable = true override val isRepeatable = true
// In this operator we process selection by ourselves. This is necessary for rider, VIM-1758
override fun postProcessSelection(): Boolean {
return false
}
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
setOperatorFunction(this) setOperatorFunction(this)
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)

View File

@ -217,8 +217,6 @@ private object FileTypePatterns {
return if (fileTypeName in htmlLikeFileTypes) { return if (fileTypeName in htmlLikeFileTypes) {
this.htmlPatterns this.htmlPatterns
} else if (fileTypeName == "JAVA" || fileExtension == "java") {
this.javaPatterns
} else if (fileTypeName == "Ruby" || fileExtension == "rb") { } else if (fileTypeName == "Ruby" || fileExtension == "rb") {
this.rubyPatterns this.rubyPatterns
} else if (fileTypeName == "RHTML" || fileExtension == "erb") { } else if (fileTypeName == "RHTML" || fileExtension == "erb") {
@ -244,7 +242,6 @@ private object FileTypePatterns {
) )
private val htmlPatterns = createHtmlPatterns() private val htmlPatterns = createHtmlPatterns()
private val javaPatterns = createJavaPatterns()
private val rubyPatterns = createRubyPatterns() private val rubyPatterns = createRubyPatterns()
private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns private val rubyAndHtmlPatterns = rubyPatterns + htmlPatterns
private val phpPatterns = createPhpPatterns() private val phpPatterns = createPhpPatterns()
@ -274,14 +271,6 @@ private object FileTypePatterns {
) )
} }
private fun createJavaPatterns(): LanguagePatterns {
return (
LanguagePatterns("\\b(?<!else\\s+)if\\b", "\\belse\\s+if\\b", "\\belse(?!\\s+if)\\b") +
LanguagePatterns("\\bdo\\b", "\\bwhile\\b") +
LanguagePatterns("\\btry\\b", "\\bcatch\\b", "\\bfinally\\b")
)
}
private fun createRubyPatterns(): LanguagePatterns { private fun createRubyPatterns(): LanguagePatterns {
// Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim // Original patterns: https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim
// We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries. // We use non-capturing groups (?:) since we don't need any back refs. The \\b marker takes care of word boundaries.

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.extension.surround package com.maddyhome.idea.vim.extension.surround
import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor 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
@ -256,7 +255,6 @@ internal class VimSurroundExtension : VimExtension {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
// Deleting surround is just changing the surrounding to "nothing" // Deleting surround is just changing the surrounding to "nothing"
val charFrom = getChar(editor.ij) val charFrom = getChar(editor.ij)
LOG.debug("DSurroundHandler: charFrom = $charFrom")
if (charFrom.code == 0) return if (charFrom.code == 0) return
runWriteAction { CSurroundHandler.change(editor, context, charFrom, null) } runWriteAction { CSurroundHandler.change(editor, context, charFrom, null) }
@ -309,10 +307,8 @@ internal class VimSurroundExtension : VimExtension {
} }
} }
} }
}
private val LOG = logger<VimSurroundExtension>()
companion object {
private const val REGISTER = '"' private const val REGISTER = '"'
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern() private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
@ -372,13 +368,11 @@ private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = whe
private fun getChar(editor: Editor): Char { private fun getChar(editor: Editor): Char {
val key = inputKeyStroke(editor) val key = inputKeyStroke(editor)
val keyChar = key.keyChar val keyChar = key.keyChar
val res = if (keyChar == KeyEvent.CHAR_UNDEFINED || keyChar.code == KeyEvent.VK_ESCAPE) { return if (keyChar == KeyEvent.CHAR_UNDEFINED || keyChar.code == KeyEvent.VK_ESCAPE) {
0.toChar() 0.toChar()
} else { } else {
keyChar keyChar
} }
LOG.trace("getChar: $res")
return res
} }
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) { private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
@ -401,9 +395,8 @@ private fun performSurround(pair: Pair<String, String>, range: TextRange, caret:
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)
injector.markService.setChangeMarks( injector.markService.setChangeMarks(caret, TextRange(range.startOffset, range.endOffset + leftSurround.length + rightSurround.length))
caret, }
TextRange(range.startOffset, range.endOffset + leftSurround.length + rightSurround.length) }
)
} }
} }

View File

@ -20,6 +20,9 @@ import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.event.EditorMouseEvent import com.intellij.openapi.editor.event.EditorMouseEvent
import com.intellij.openapi.editor.event.EditorMouseListener import com.intellij.openapi.editor.event.EditorMouseListener
import com.intellij.openapi.editor.impl.TextRangeInterval import com.intellij.openapi.editor.impl.TextRangeInterval
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.UserDataHolder import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.CodeStyleManager
@ -62,6 +65,7 @@ import com.maddyhome.idea.vim.helper.endOffsetInclusive
import com.maddyhome.idea.vim.helper.inInsertMode import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.icons.VimIcons
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
import com.maddyhome.idea.vim.listener.VimInsertListener import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
@ -85,6 +89,7 @@ import kotlin.math.min
*/ */
public class ChangeGroup : VimChangeGroupBase() { public class ChangeGroup : VimChangeGroupBase() {
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>() private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
private var lastShownTime = 0L
private val listener: EditorMouseListener = object : EditorMouseListener { private val listener: EditorMouseListener = object : EditorMouseListener {
override fun mouseClicked(event: EditorMouseEvent) { override fun mouseClicked(event: EditorMouseEvent) {
val editor = event.editor val editor = event.editor
@ -98,6 +103,10 @@ public class ChangeGroup : VimChangeGroupBase() {
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable) EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
} }
public fun editorReleased(editor: Editor?) {
EventFacade.getInstance().removeEditorMouseListener(editor!!, listener)
}
override fun type(vimEditor: VimEditor, context: ExecutionContext, key: Char) { override fun type(vimEditor: VimEditor, context: ExecutionContext, key: Char) {
val editor = (vimEditor as IjVimEditor).editor val editor = (vimEditor as IjVimEditor).editor
val ijContext = context.ij val ijContext = context.ij
@ -636,6 +645,25 @@ public class ChangeGroup : VimChangeGroupBase() {
avalanche: Boolean, avalanche: Boolean,
): Boolean { ): Boolean {
// Just an easter egg
if (avalanche) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastShownTime > 60000) {
lastShownTime = currentTime
ApplicationManager.getApplication().invokeLater {
val balloon = JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(
"Wow, nice vim skills!", VimIcons.IDEAVIM,
MessageType.INFO.titleForeground, MessageType.INFO.popupBackground,
null
).createBalloon()
balloon.show(
JBPopupFactory.getInstance().guessBestPopupLocation((editor as IjVimEditor).editor),
Balloon.Position.below
)
}
}
}
val nf: List<String> = injector.options(editor).nrformats val nf: List<String> = injector.options(editor).nrformats
val alpha = nf.contains("alpha") val alpha = nf.contains("alpha")
val hex = nf.contains("hex") val hex = nf.contains("hex")

View File

@ -29,8 +29,6 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys) public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids) public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
public var visualdelay: Int by optionProperty(IjOptions.visualdelay) public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
public var colorfulmodewidget: Boolean by optionProperty(IjOptions.colorfulmodewidget)
// Temporary options to control work-in-progress behaviour // Temporary options to control work-in-progress behaviour
public var oldundo: Boolean by optionProperty(IjOptions.oldundo) public var oldundo: Boolean by optionProperty(IjOptions.oldundo)

View File

@ -86,8 +86,6 @@ public object IjOptions {
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true)) public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true))
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true)) public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true))
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true)) public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true))
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true))
public val colorfulmodewidget: ToggleOption = addOption(ToggleOption("colorfulmodewidget", GLOBAL, "colorfulmodewidget", false, isTemporary = true))
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which // This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
// derives from Option<VimInt> // derives from Option<VimInt>

View File

@ -64,10 +64,8 @@ internal class MacroGroup : VimMacroBase() {
try { try {
myPotemkinProgress.text2 = if (isInternalMacro) "Executing internal macro" else "" myPotemkinProgress.text2 = if (isInternalMacro) "Executing internal macro" else ""
val runnable = runnable@{ val runnable = runnable@{
try {
// Handle one keystroke then queue up the next key // Handle one keystroke then queue up the next key
for (i in 0 until total) { for (i in 0 until total) {
try {
myPotemkinProgress.fraction = (i + 1).toDouble() / total myPotemkinProgress.fraction = (i + 1).toDouble() / total
while (keyStack.hasStroke()) { while (keyStack.hasStroke()) {
val key = keyStack.feedStroke() val key = keyStack.feedStroke()
@ -77,20 +75,14 @@ internal class MacroGroup : VimMacroBase() {
return@runnable return@runnable
} }
ProgressManager.getInstance().executeNonCancelableSection { ProgressManager.getInstance().executeNonCancelableSection {
// Prevent autocompletion during macros.
// See https://github.com/JetBrains/ideavim/pull/772 for details
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion) CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion)
getInstance().handleKey(editor, key, context) getInstance().handleKey(editor, key, context)
} }
if (injector.messages.isError()) return@runnable if (injector.messages.isError()) return@runnable
} }
} finally {
keyStack.resetFirst() keyStack.resetFirst()
} }
}
} finally {
keyStack.removeFirst() keyStack.removeFirst()
}
if (!isInternalMacro) { if (!isInternalMacro) {
MacroAutoImport.run(editor.ij, context.ij) MacroAutoImport.run(editor.ij, context.ij)
} }

View File

@ -48,7 +48,9 @@ import com.maddyhome.idea.vim.api.options
import com.maddyhome.idea.vim.api.visualLineToBufferLine import com.maddyhome.idea.vim.api.visualLineToBufferLine
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.handler.Motion import com.maddyhome.idea.vim.handler.Motion
@ -72,8 +74,6 @@ import com.maddyhome.idea.vim.newapi.IjVimCaret
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
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.mode.Mode
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import org.jetbrains.annotations.Range import org.jetbrains.annotations.Range
import java.io.File import java.io.File
@ -461,7 +461,6 @@ internal class MotionGroup : VimMotionGroupBase() {
val fileEditor = event.oldEditor val fileEditor = event.oldEditor
if (fileEditor is TextEditor) { if (fileEditor is TextEditor) {
val editor = fileEditor.editor val editor = fileEditor.editor
if (!editor.isDisposed) {
ExOutputModel.getInstance(editor).clear() ExOutputModel.getInstance(editor).clear()
editor.vim.let { vimEditor -> editor.vim.let { vimEditor ->
if (VimStateMachine.getInstance(vimEditor).mode is Mode.VISUAL) { if (VimStateMachine.getInstance(vimEditor).mode is Mode.VISUAL) {
@ -473,4 +472,3 @@ internal class MotionGroup : VimMotionGroupBase() {
} }
} }
} }
}

View File

@ -40,6 +40,10 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
override fun getGlobalIjOptions() = GlobalIjOptions(OptionAccessScope.GLOBAL(null)) override fun getGlobalIjOptions() = GlobalIjOptions(OptionAccessScope.GLOBAL(null))
override fun getEffectiveIjOptions(editor: VimEditor) = EffectiveIjOptions(OptionAccessScope.EFFECTIVE(editor)) override fun getEffectiveIjOptions(editor: VimEditor) = EffectiveIjOptions(OptionAccessScope.EFFECTIVE(editor))
private fun updateFallbackWindow(fallbackWindow: VimEditor, targetEditor: VimEditor) {
copyPerWindowGlobalValues(fallbackWindow, targetEditor)
}
companion object { companion object {
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) { fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
// Vim only has one window, and it's not possible to close it. This means that editing a new file will always // Vim only has one window, and it's not possible to close it. This means that editing a new file will always
@ -54,8 +58,6 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
// Unfortunately, we can't reliably know if a closing editor is the selected editor. Instead, we rely on selection // Unfortunately, we can't reliably know if a closing editor is the selected editor. Instead, we rely on selection
// change events. If an editor is losing selection and there is no new selection, we can assume this means that // change events. If an editor is losing selection and there is no new selection, we can assume this means that
// the last editor has been closed, and use the closed editor to update the fallback window // the last editor has been closed, and use the closed editor to update the fallback window
//
// XXX: event.oldEditor will must probably return a disposed editor. So, it should be treated with care
if (event.newEditor == null) { if (event.newEditor == null) {
(event.oldEditor as? TextEditor)?.editor?.let { (event.oldEditor as? TextEditor)?.editor?.let {
(VimPlugin.getOptionGroup() as OptionGroup).updateFallbackWindow(injector.fallbackWindow, it.vim) (VimPlugin.getOptionGroup() as OptionGroup).updateFallbackWindow(injector.fallbackWindow, it.vim)
@ -66,7 +68,7 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
} }
internal class IjOptionConstants { internal class IjOptionConstants {
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName") @Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate")
companion object { companion object {
const val idearefactormode_keep = "keep" const val idearefactormode_keep = "keep"

View File

@ -192,8 +192,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
* @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
*/ */
@Override @TestOnly
public void setLastSearchState(@SuppressWarnings("unused") @NotNull VimEditor editor, @NotNull String pattern, public void setLastSearchState(@SuppressWarnings("unused") @NotNull Editor editor, @NotNull String pattern,
@NotNull String patternOffset, Direction direction) { @NotNull String patternOffset, Direction direction) {
setLastUsedPattern(pattern, RE_SEARCH, true); setLastUsedPattern(pattern, RE_SEARCH, true);
lastIgnoreSmartCase = false; lastIgnoreSmartCase = false;

View File

@ -205,7 +205,7 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
* @param event The change event * @param event The change event
*/ */
override fun beforeDocumentChange(event: DocumentEvent) { override fun beforeDocumentChange(event: DocumentEvent) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event") if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event")
if (event.oldLength == 0) return if (event.oldLength == 0) return
val doc = event.document val doc = event.document
@ -221,7 +221,7 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
* @param event The change event * @param event The change event
*/ */
override fun documentChanged(event: DocumentEvent) { override fun documentChanged(event: DocumentEvent) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event") if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event")
if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return
val doc = event.document val doc = event.document
@ -242,7 +242,7 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
class VimBookmarksListener(private val myProject: Project) : BookmarksListener { class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
override fun bookmarkAdded(group: BookmarkGroup, bookmark: Bookmark) { override fun bookmarkAdded(group: BookmarkGroup, bookmark: Bookmark) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
if (!injector.globalIjOptions().ideamarks) { if (!injector.globalIjOptions().ideamarks) {
return return
} }
@ -255,7 +255,7 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
} }
override fun bookmarkRemoved(group: BookmarkGroup, bookmark: Bookmark) { override fun bookmarkRemoved(group: BookmarkGroup, bookmark: Bookmark) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
if (!injector.globalIjOptions().ideamarks) { if (!injector.globalIjOptions().ideamarks) {
return return
} }

View File

@ -27,12 +27,15 @@ import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.isBlock
import com.maddyhome.idea.vim.state.mode.isChar
import com.maddyhome.idea.vim.state.mode.isLine
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.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.RWLockLabel import com.maddyhome.idea.vim.helper.RWLockLabel
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.ide.isClionNova
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.newapi.IjVimCaret import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.IjVimEditor
@ -45,10 +48,6 @@ import com.maddyhome.idea.vim.put.PutData
import com.maddyhome.idea.vim.put.VimPasteProvider import com.maddyhome.idea.vim.put.VimPasteProvider
import com.maddyhome.idea.vim.put.VimPutBase import com.maddyhome.idea.vim.put.VimPutBase
import com.maddyhome.idea.vim.register.RegisterConstants import com.maddyhome.idea.vim.register.RegisterConstants
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.isBlock
import com.maddyhome.idea.vim.state.mode.isChar
import com.maddyhome.idea.vim.state.mode.isLine
import java.awt.datatransfer.DataFlavor import java.awt.datatransfer.DataFlavor
internal class PutGroup : VimPutBase() { internal class PutGroup : VimPutBase() {
@ -190,7 +189,7 @@ internal class PutGroup : VimPutBase() {
endOffset: Int, endOffset: Int,
): Int { ): Int {
// Temp fix for VIM-2808. Should be removed after rider will fix it's issues // Temp fix for VIM-2808. Should be removed after rider will fix it's issues
if (PlatformUtils.isRider() || isClionNova()) return endOffset if (PlatformUtils.isRider()) return endOffset
val startLine = editor.offsetToBufferPosition(startOffset).line val startLine = editor.offsetToBufferPosition(startOffset).line
val endLine = editor.offsetToBufferPosition(endOffset - 1).line val endLine = editor.offsetToBufferPosition(endOffset - 1).line

View File

@ -40,15 +40,9 @@ internal object IdeaSelectionControl {
* This method should be in sync with [predictMode] * This method should be in sync with [predictMode]
* *
* Control unexpected (non vim) selection change and adjust a mode to it. The new mode is not enabled immediately, * Control unexpected (non vim) selection change and adjust a mode to it. The new mode is not enabled immediately,
* but with some delay (using [VimVisualTimer]). The delay is used because some platform functionality * but with some delay (using [VimVisualTimer])
* makes features by using selection. E.g. PyCharm unindent firstly select the indenting then applies delete action.
* Such "quick" selection breaks IdeaVim behaviour.
* *
* See [VimVisualTimer] to more info. * See [VimVisualTimer] to more info.
*
* XXX: This method can be split into "change calculation" and "change apply". In this way, we would be able
* to calculate if we need to make a change or not and reduce the number of these calls.
* If this refactoring ever is applied, please add `assertNull(VimVisualTimer.timer)` to `tearDown` of VimTestCase.
*/ */
fun controlNonVimSelectionChange( fun controlNonVimSelectionChange(
editor: Editor, editor: Editor,
@ -56,7 +50,6 @@ internal object IdeaSelectionControl {
) { ) {
VimVisualTimer.singleTask(editor.vim.mode) { initialMode -> VimVisualTimer.singleTask(editor.vim.mode) { initialMode ->
if (VimPlugin.isNotEnabled()) return@singleTask
if (editor.isIdeaVimDisabledHere) return@singleTask if (editor.isIdeaVimDisabledHere) return@singleTask
logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode") logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode")
@ -128,9 +121,8 @@ internal object IdeaSelectionControl {
} }
} }
private fun dontChangeMode(editor: Editor): Boolean { private fun dontChangeMode(editor: Editor): Boolean =
return editor.isTemplateActive() && (editor.vim.isIdeaRefactorModeKeep || editor.vim.mode.hasVisualSelection) editor.isTemplateActive() && (editor.vim.isIdeaRefactorModeKeep || editor.vim.mode.hasVisualSelection)
}
private fun chooseNonSelectionMode(editor: Editor): Mode { private fun chooseNonSelectionMode(editor: Editor): Mode {
val templateActive = editor.isTemplateActive() val templateActive = editor.isTemplateActive()

View File

@ -9,10 +9,10 @@
package com.maddyhome.idea.vim.group.visual package com.maddyhome.idea.vim.group.visual
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.state.mode.Mode
import java.awt.event.ActionEvent import java.awt.event.ActionEvent
import javax.swing.Timer import javax.swing.Timer
@ -79,11 +79,6 @@ internal object VimVisualTimer {
} }
} }
fun drop() {
swingTimer?.stop()
swingTimer = null
}
inline fun timerAction(task: (initialMode: Mode?) -> Unit) { inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
task(mode) task(mode)
swingTimer = null swingTimer = null

View File

@ -11,68 +11,41 @@ package com.maddyhome.idea.vim.handler
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.KeyboardShortcut import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.actionSystem.Shortcut import com.intellij.openapi.actionSystem.Shortcut
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.keymap.Keymap import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapManagerListener import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx import com.intellij.openapi.keymap.ex.KeymapManagerEx
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.StartupActivity
import com.intellij.util.SingleAlarm
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.api.key import com.maddyhome.idea.vim.api.key
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import javax.swing.KeyStroke import javax.swing.KeyStroke
// We use alarm with delay to avoid many notifications in case many events are fired at the same time // We use alarm with delay to avoid many notifications in case many events are fired at the same time
internal val keyCheckRequests = MutableSharedFlow<Unit>(replay=1, onBufferOverflow = BufferOverflow.DROP_OLDEST) // [VERSION UPDATE] 2023.3+ Replace SingleAlarm with coroutine flows https://youtrack.jetbrains.com/articles/IJPL-A-8/Alarm-Alternative
internal val keymapCheckRequester = SingleAlarm({ verifyKeymap() }, 5_000)
/** /**
* This checker verifies that the keymap has a correct configuration that is required for IdeaVim plugin * This checker verifies that the keymap has a correct configuration that is required for IdeaVim plugin
*/ */
internal class KeymapChecker : ProjectActivity { internal class KeymapChecker : StartupActivity {
override suspend fun execute(project: Project) { override fun runActivity(project: Project) {
project.service<KeymapCheckerService>().start() keymapCheckRequester.request()
keyCheckRequests.emit(Unit)
}
}
/**
* At the moment of release 2023.3 there is a problem that starting a coroutine like this
* right in the project activity will block this project activity in tests.
* To avoid that, there is an intermediate service that will allow to avoid this issue.
*
* However, in general we should start this coroutine right in the [KeymapChecker]
*/
@OptIn(FlowPreview::class)
@Service(Service.Level.PROJECT)
internal class KeymapCheckerService(private val cs: CoroutineScope) {
fun start() {
cs.launch {
keyCheckRequests
.debounce(5_000)
.collectLatest { verifyKeymap() }
}
} }
} }
internal class IdeaVimKeymapChangedListener : KeymapManagerListener { internal class IdeaVimKeymapChangedListener : KeymapManagerListener {
override fun activeKeymapChanged(keymap: Keymap?) { override fun activeKeymapChanged(keymap: Keymap?) {
check(keyCheckRequests.tryEmit(Unit)) keymapCheckRequester.request()
} }
override fun shortcutChanged(keymap: Keymap, actionId: String) { override fun shortcutChanged(keymap: Keymap, actionId: String) {
check(keyCheckRequests.tryEmit(Unit)) keymapCheckRequester.request()
} }
override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) { override fun shortcutChanged(keymap: Keymap, actionId: String, fromSettings: Boolean) {
check(keyCheckRequests.tryEmit(Unit)) keymapCheckRequester.request()
} }
} }

View File

@ -125,7 +125,7 @@ internal abstract class OctopusHandler(private val nextHandler: EditorActionHand
} }
private fun isThisHandlerEnabled(editor: Editor, caret: Caret?, dataContext: DataContext?): Boolean { private fun isThisHandlerEnabled(editor: Editor, caret: Caret?, dataContext: DataContext?): Boolean {
if (VimPlugin.isNotEnabled()) return false if (!VimPlugin.isEnabled()) return false
if (!isHandlerEnabled(editor, dataContext)) return false if (!isHandlerEnabled(editor, dataContext)) return false
if (isNotActualKeyPress(dataContext)) return false if (isNotActualKeyPress(dataContext)) return false
return true return true
@ -229,7 +229,7 @@ internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(n
} }
/** /**
* Rider (and CLion Nova) uses a separate handler for esc to close the completion. IdeaOnlyEscapeHandlerAction is especially * Rider uses a separate handler for esc to close the completion. IdeaOnlyEscapeHandlerAction is especially
* designer to get all the esc presses, and if there is a completion close it and do not pass the execution further. * designer to get all the esc presses, and if there is a completion close it and do not pass the execution further.
* This doesn't work the same as in IJ. * This doesn't work the same as in IJ.
* In IdeaVim, we'd like to exit insert mode on closing completion. This is a requirement as the change of this * In IdeaVim, we'd like to exit insert mode on closing completion. This is a requirement as the change of this

View File

@ -81,7 +81,7 @@ private fun Editor.guicursorMode(): GuiCursorMode {
private fun isBlockCursorOverride() = EditorSettingsExternalizable.getInstance().isBlockCursor private fun isBlockCursorOverride() = EditorSettingsExternalizable.getInstance().isBlockCursor
private fun Editor.updatePrimaryCaretVisualAttributes() { private fun Editor.updatePrimaryCaretVisualAttributes() {
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled") if (!VimPlugin.isEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this) caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking // Make sure the caret is visible as soon as it's set. It might be invisible while blinking
@ -89,7 +89,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
} }
private fun Editor.updateSecondaryCaretsVisualAttributes() { private fun Editor.updateSecondaryCaretsVisualAttributes() {
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled") if (!VimPlugin.isEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
// IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them // IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them
val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this) val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
this.caretModel.allCarets.forEach { this.caretModel.allCarets.forEach {

View File

@ -110,7 +110,7 @@ internal fun Editor.isTemplateActive(): Boolean {
} }
private fun vimEnabled(editor: Editor?): Boolean { private fun vimEnabled(editor: Editor?): Boolean {
if (VimPlugin.isNotEnabled()) return false if (!VimPlugin.isEnabled()) return false
if (editor != null && editor.isIdeaVimDisabledHere) return false if (editor != null && editor.isIdeaVimDisabledHere) return false
return true return true
} }

View File

@ -143,7 +143,7 @@ internal class IjActionExecutor : VimActionExecutor {
manager.fireAfterActionPerformed(action, event, result!!) manager.fireAfterActionPerformed(action, event, result!!)
} }
if (indexError != null) { if (indexError != null) {
ActionUtil.showDumbModeWarning(project, action, event) ActionUtil.showDumbModeWarning(project, event)
} }
} }

View File

@ -124,6 +124,10 @@ internal var Editor.vimMorePanel: ExOutputPanel? by userData()
internal var Editor.vimExOutput: ExOutputModel? by userData() internal var Editor.vimExOutput: ExOutputModel? by userData()
internal var Editor.vimTestInputModel: TestInputModel? by userData() internal var Editor.vimTestInputModel: TestInputModel? by userData()
/**
* Checks whether a keeping visual mode visual operator action is performed on editor.
*/
internal var Editor.vimKeepingVisualOperatorAction: Boolean by userDataOr { false }
internal var Editor.vimChangeActionSwitchMode: Mode? by userData() internal var Editor.vimChangeActionSwitchMode: Mode? by userData()
/** /**

View File

@ -1,25 +0,0 @@
/*
* 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.ide
import com.intellij.openapi.extensions.ExtensionPointName
internal val clionEP = ExtensionPointName.create<ClionNovaProvider>("IdeaVIM.clionNovaProvider")
internal interface ClionNovaProvider {
fun isClionNova(): Boolean
}
internal class ClionNovaProviderImpl : ClionNovaProvider {
override fun isClionNova(): Boolean = true
}
internal fun isClionNova(): Boolean {
return clionEP.extensions.any { it.isClionNova() }
}

View File

@ -40,7 +40,7 @@ internal object AppCodeTemplates {
private var editor: Editor? = null private var editor: Editor? = null
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
if (hostEditor != null) { if (hostEditor != null) {
@ -49,7 +49,7 @@ internal object AppCodeTemplates {
} }
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
if (ActionManager.getInstance().getId(action) == IdeActions.ACTION_CHOOSE_LOOKUP_ITEM) { if (ActionManager.getInstance().getId(action) == IdeActions.ACTION_CHOOSE_LOOKUP_ITEM) {
val myEditor = editor val myEditor = editor

View File

@ -59,7 +59,7 @@ internal object IdeaSpecifics {
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) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
if (hostEditor != null) { if (hostEditor != null) {
@ -92,7 +92,7 @@ internal object IdeaSpecifics {
} }
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val editor = editor val editor = editor
if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) { if (editor != null && action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
@ -138,7 +138,7 @@ internal object IdeaSpecifics {
//region Enter insert mode for surround templates without selection //region Enter insert mode for surround templates without selection
class VimTemplateManagerListener : TemplateManagerListener { class VimTemplateManagerListener : TemplateManagerListener {
override fun templateStarted(state: TemplateState) { override fun templateStarted(state: TemplateState) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val editor = state.editor ?: return val editor = state.editor ?: return
state.addTemplateStateListener(object : TemplateEditingAdapter() { state.addTemplateStateListener(object : TemplateEditingAdapter() {
@ -176,7 +176,7 @@ internal object IdeaSpecifics {
//region Register shortcuts for lookup and perform partial reset //region Register shortcuts for lookup and perform partial reset
class LookupTopicListener : LookupManagerListener { class LookupTopicListener : LookupManagerListener {
override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) { override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
// Lookup opened // Lookup opened
if (oldLookup == null && newLookup is LookupImpl) { if (oldLookup == null && newLookup is LookupImpl) {
@ -199,7 +199,7 @@ internal object IdeaSpecifics {
//region Hide Vim search highlights when showing IntelliJ search results //region Hide Vim search highlights when showing IntelliJ search results
class VimFindModelListener : FindModelListener { class VimFindModelListener : FindModelListener {
override fun findNextModelChanged() { override fun findNextModelChanged() {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
VimPlugin.getSearch().clearSearchHighlight() VimPlugin.getSearch().clearSearchHighlight()
} }
} }

View File

@ -27,7 +27,7 @@ internal class RiderActionListener : AnActionListener {
private var editor: Editor? = null private var editor: Editor? = null
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) { override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR) val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
if (hostEditor != null) { if (hostEditor != null) {
@ -36,7 +36,7 @@ internal class RiderActionListener : AnActionListener {
} }
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) { override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
//region Extend Selection for Rider //region Extend Selection for Rider
when (ActionManager.getInstance().getId(action)) { when (ActionManager.getInstance().getId(action)) {

View File

@ -9,7 +9,6 @@
package com.maddyhome.idea.vim.listener package com.maddyhome.idea.vim.listener
import com.intellij.ide.ui.UISettings import com.intellij.ide.ui.UISettings
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.trace import com.intellij.openapi.diagnostic.trace
@ -29,9 +28,8 @@ import com.intellij.openapi.editor.event.EditorMouseMotionListener
import com.intellij.openapi.editor.event.SelectionEvent import com.intellij.openapi.editor.event.SelectionEvent
import com.intellij.openapi.editor.event.SelectionListener import com.intellij.openapi.editor.event.SelectionListener
import com.intellij.openapi.editor.ex.DocumentEx import com.intellij.openapi.editor.ex.DocumentEx
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
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
@ -42,11 +40,14 @@ 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.VimKeyListener import com.maddyhome.idea.vim.VimKeyListener
@ -61,7 +62,6 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.group.EditorGroup import com.maddyhome.idea.vim.group.EditorGroup
import com.maddyhome.idea.vim.group.FileGroup import com.maddyhome.idea.vim.group.FileGroup
import com.maddyhome.idea.vim.group.IjOptions
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.OptionGroup import com.maddyhome.idea.vim.group.OptionGroup
import com.maddyhome.idea.vim.group.ScrollGroup import com.maddyhome.idea.vim.group.ScrollGroup
@ -71,7 +71,7 @@ import com.maddyhome.idea.vim.group.visual.VimVisualTimer
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.correctorRequester import com.maddyhome.idea.vim.handler.correctorRequester
import com.maddyhome.idea.vim.handler.keyCheckRequests import com.maddyhome.idea.vim.handler.keymapCheckRequester
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.exitSelectMode import com.maddyhome.idea.vim.helper.exitSelectMode
@ -97,9 +97,6 @@ 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.ui.ShowCmdOptionChangeListener import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
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
@ -135,7 +132,7 @@ internal object VimListenerManager {
GlobalListeners.enable() GlobalListeners.enable()
EditorListeners.addAll() EditorListeners.addAll()
correctorRequester.request() correctorRequester.request()
check(keyCheckRequests.tryEmit(Unit)) keymapCheckRequester.request()
} }
fun turnOff() { fun turnOff() {
@ -159,13 +156,6 @@ internal object VimListenerManager {
optionGroup.addEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE) optionGroup.addEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
optionGroup.addEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener) optionGroup.addEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener) optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
// This code is executed after ideavimrc execution, so we trigger onGlobalOptionChanged just in case
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
modeWidgetOptionListener.onGlobalOptionChanged()
macroWidgetOptionListener.onGlobalOptionChanged()
optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener) optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable) EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable)
@ -173,8 +163,6 @@ internal object VimListenerManager {
busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener) busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener)
EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable) EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable)
val eventMulticaster = EditorFactory.getInstance().eventMulticaster as? EditorEventMulticasterEx
eventMulticaster?.addFocusChangeListener(VimFocusListener, VimPlugin.getInstance().onOffDisposable)
} }
fun disable() { fun disable() {
@ -185,8 +173,6 @@ internal object VimListenerManager {
optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE) optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener) optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener) optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener) optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
} }
} }
@ -229,67 +215,49 @@ internal object VimListenerManager {
} }
fun add(editor: Editor, openingEditor: VimEditor, scenario: LocalOptionInitialisationScenario) { fun add(editor: Editor, openingEditor: VimEditor, scenario: LocalOptionInitialisationScenario) {
// 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()
val disposable = editor.project?.vimDisposable ?: return val disposable =
Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
val listenersDisposable = Disposer.newDisposable(disposable)
editor.putUserData(editorListenersDisposable, listenersDisposable)
Disposer.register(listenersDisposable) {
if (VimListenerTestObject.enabled) {
VimListenerTestObject.disposedCounter += 1
}
}
editor.contentComponent.addKeyListener(VimKeyListener) editor.contentComponent.addKeyListener(VimKeyListener)
Disposer.register(listenersDisposable) { editor.contentComponent.removeKeyListener(VimKeyListener) } Disposer.register(disposable) { editor.contentComponent.removeKeyListener(VimKeyListener) }
// Initialise the local options. We MUST do this before anything has the chance to query options // Initialise the local options. We MUST do this before anything has the chance to query options
val vimEditor = editor.vim VimPlugin.getOptionGroup().initialiseLocalOptions(editor.vim, openingEditor, scenario)
VimPlugin.getOptionGroup().initialiseLocalOptions(vimEditor, openingEditor, scenario)
val eventFacade = EventFacade.getInstance() val eventFacade = EventFacade.getInstance()
eventFacade.addEditorMouseListener(editor, EditorMouseHandler, listenersDisposable) eventFacade.addEditorMouseListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, listenersDisposable) eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler, disposable)
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, listenersDisposable) eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler, disposable)
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, listenersDisposable) eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener, disposable)
eventFacade.addCaretListener(editor, EditorCaretHandler, listenersDisposable) eventFacade.addCaretListener(editor, EditorCaretHandler, disposable)
VimPlugin.getEditor().editorCreated(editor) VimPlugin.getEditor().editorCreated(editor)
VimPlugin.getChange().editorCreated(editor, listenersDisposable) VimPlugin.getChange().editorCreated(editor, disposable)
injector.listenersNotifier.notifyEditorCreated(vimEditor) Disposer.register(disposable) {
Disposer.register(listenersDisposable) {
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true) VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
} }
} }
fun remove(editor: Editor, isReleased: Boolean) { fun remove(editor: Editor, isReleased: Boolean) {
val editorDisposable = editor.getUserData(editorListenersDisposable) editor.contentComponent.removeKeyListener(VimKeyListener)
if (editorDisposable != null) { val eventFacade = EventFacade.getInstance()
Disposer.dispose(editorDisposable) eventFacade.removeEditorMouseListener(editor, EditorMouseHandler)
} eventFacade.removeEditorMouseMotionListener(editor, EditorMouseHandler)
else StrictMode.fail("Editor doesn't have disposable attached. $editor") eventFacade.removeEditorSelectionListener(editor, EditorSelectionHandler)
eventFacade.removeComponentMouseListener(editor.contentComponent, ComponentMouseListener)
eventFacade.removeCaretListener(editor, EditorCaretHandler)
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased) VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
}
VimPlugin.getChange().editorReleased(editor)
} }
private object VimFocusListener : FocusChangeListener {
override fun focusGained(editor: Editor) {
injector.listenersNotifier.notifyEditorFocusGained(editor.vim)
} }
override fun focusLost(editor: Editor) {
injector.listenersNotifier.notifyEditorFocusLost(editor.vim)
}
}
val editorListenersDisposable = Key.create<Disposable>("IdeaVim listeners disposable")
object VimCaretListener : CaretListener { object VimCaretListener : CaretListener {
override fun caretAdded(event: CaretEvent) { override fun caretAdded(event: CaretEvent) {
if (vimDisabled(event.editor)) return if (vimDisabled(event.editor)) return
@ -304,7 +272,7 @@ internal object VimListenerManager {
class VimFileEditorManagerListener : FileEditorManagerListener { class VimFileEditorManagerListener : FileEditorManagerListener {
override fun selectionChanged(event: FileEditorManagerEvent) { override fun selectionChanged(event: FileEditorManagerEvent) {
if (VimPlugin.isNotEnabled()) return if (!VimPlugin.isEnabled()) return
val newEditor = event.newEditor val newEditor = event.newEditor
if (newEditor is TextEditor) { if (newEditor is TextEditor) {
@ -382,15 +350,13 @@ internal object VimListenerManager {
} }
override fun editorReleased(event: EditorFactoryEvent) { override fun editorReleased(event: EditorFactoryEvent) {
val vimEditor = event.editor.vim injector.markService.editorReleased(event.editor.vim)
injector.listenersNotifier.notifyEditorReleased(vimEditor)
injector.markService.editorReleased(vimEditor)
} }
override fun fileOpenedSync( override fun fileOpenedSync(
source: FileEditorManager, source: FileEditorManager,
file: VirtualFile, file: VirtualFile,
editorsWithProviders: List<FileEditorWithProvider>, editorsWithProviders: List<FileEditorWithProvider>
) { ) {
// This callback is called once all editors are created for a file being opened. The EditorComposite has been // This callback is called once all editors are created for a file being opened. The EditorComposite has been
// created (and the list of editors and providers is passed here) and added to an EditorWindow tab, inside a // created (and the list of editors and providers is passed here) and added to an EditorWindow tab, inside a
@ -448,7 +414,6 @@ internal object VimListenerManager {
*/ */
override fun selectionChanged(selectionEvent: SelectionEvent) { override fun selectionChanged(selectionEvent: SelectionEvent) {
if (selectionEvent.editor.isIdeaVimDisabledHere) return if (selectionEvent.editor.isIdeaVimDisabledHere) return
VimVisualTimer.drop()
val editor = selectionEvent.editor val editor = selectionEvent.editor
val document = editor.document val document = editor.document
val ijVimEditor = IjVimEditor(editor) val ijVimEditor = IjVimEditor(editor)
@ -742,11 +707,6 @@ internal object VimListenerManager {
} }
} }
internal object VimListenerTestObject {
var enabled: Boolean = false
var disposedCounter = 0
}
private object MouseEventsDataHolder { private object MouseEventsDataHolder {
const val skipEvents = 3 const val skipEvents = 3
var skipNDragEvents = skipEvents var skipNDragEvents = skipEvents

View File

@ -54,6 +54,7 @@ import com.maddyhome.idea.vim.helper.isTemplateActive
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
import com.maddyhome.idea.vim.helper.updateCaretsVisualPosition import com.maddyhome.idea.vim.helper.updateCaretsVisualPosition
import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode
import com.maddyhome.idea.vim.helper.vimKeepingVisualOperatorAction
import com.maddyhome.idea.vim.helper.vimLastSelectionType import com.maddyhome.idea.vim.helper.vimLastSelectionType
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
@ -81,6 +82,11 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
set(value) { set(value) {
editor.vimChangeActionSwitchMode = value editor.vimChangeActionSwitchMode = value
} }
override var vimKeepingVisualOperatorAction: Boolean
get() = editor.vimKeepingVisualOperatorAction
set(value) {
editor.vimKeepingVisualOperatorAction = value
}
override fun fileSize(): Long = editor.fileSize.toLong() override fun fileSize(): Long = editor.fileSize.toLong()

View File

@ -18,7 +18,6 @@ import com.intellij.util.IJSwingUtilities;
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.ExecutionContext; import com.maddyhome.idea.vim.api.ExecutionContext;
import com.maddyhome.idea.vim.diagnostic.VimLogger;
import com.maddyhome.idea.vim.helper.HelperKt; import com.maddyhome.idea.vim.helper.HelperKt;
import com.maddyhome.idea.vim.helper.MessageHelper; import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.UiHelper; import com.maddyhome.idea.vim.helper.UiHelper;
@ -60,8 +59,6 @@ public class ExOutputPanel extends JPanel {
private boolean myActive = false; private boolean myActive = false;
private static final VimLogger LOG = injector.getLogger(ExOutputPanel.class);
private ExOutputPanel(@NotNull Editor editor) { private ExOutputPanel(@NotNull Editor editor) {
myEditor = editor; myEditor = editor;
@ -302,10 +299,6 @@ public class ExOutputPanel extends JPanel {
final KeyStroke key = KeyStroke.getKeyStrokeForEvent(e); final KeyStroke key = KeyStroke.getKeyStrokeForEvent(e);
final List<KeyStroke> keys = new ArrayList<>(1); final List<KeyStroke> keys = new ArrayList<>(1);
keys.add(key); keys.add(key);
if (LOG.isTrace()) {
LOG.trace("Adding new keys to keyStack as part of playback. State before adding keys: " +
KeyHandler.getInstance().getKeyStack().dump());
}
KeyHandler.getInstance().getKeyStack().addKeys(keys); KeyHandler.getInstance().getKeyStack().addKeys(keys);
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(myEditor), null); ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(myEditor), null);
VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1); VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
@ -365,7 +358,7 @@ public class ExOutputPanel extends JPanel {
public static class LafListener implements LafManagerListener { public static class LafListener implements LafManagerListener {
@Override @Override
public void lookAndFeelChanged(@NotNull LafManager source) { public void lookAndFeelChanged(@NotNull LafManager source) {
if (VimPlugin.isNotEnabled()) return; if (!VimPlugin.isEnabled()) return;
// Calls updateUI on this and child components // Calls updateUI on this and child components
for (Editor editor : HelperKt.localEditors()) { for (Editor editor : HelperKt.localEditors()) {
if (!ExOutputPanel.isPanelActive(editor)) continue; if (!ExOutputPanel.isPanelActive(editor)) continue;

View File

@ -8,9 +8,6 @@
package com.maddyhome.idea.vim.ui package com.maddyhome.idea.vim.ui
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.diagnostic.trace
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.helper.isCloseKeyStroke import com.maddyhome.idea.vim.helper.isCloseKeyStroke
@ -25,19 +22,13 @@ import javax.swing.KeyStroke
* @author dhleong * @author dhleong
*/ */
public object ModalEntry { public object ModalEntry {
public val LOG: Logger = logger<ModalEntry>()
public inline fun activate(editor: VimEditor, crossinline processor: (KeyStroke) -> Boolean) { public inline fun activate(editor: VimEditor, crossinline processor: (KeyStroke) -> Boolean) {
// Firstly we pull the unfinished keys of the current mapping // Firstly we pull the unfinished keys of the current mapping
val mappingStack = KeyHandler.getInstance().keyStack val mappingStack = KeyHandler.getInstance().keyStack
LOG.trace("Dumping key stack:")
LOG.trace { mappingStack.dump() }
var stroke = mappingStack.feedSomeStroke() var stroke = mappingStack.feedSomeStroke()
while (stroke != null) { while (stroke != null) {
val result = processor(stroke) val result = processor(stroke)
if (!result) { if (!result) {
LOG.trace("Got char from mapping stack")
return return
} }
stroke = mappingStack.feedSomeStroke() stroke = mappingStack.feedSomeStroke()
@ -64,7 +55,6 @@ public object ModalEntry {
KeyHandler.getInstance().modalEntryKeys += stroke KeyHandler.getInstance().modalEntryKeys += stroke
} }
if (!processor(stroke)) { if (!processor(stroke)) {
LOG.trace("Got char from keyboard input: $stroke. Event: $e")
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this) KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this)
loop.exit() loop.exit()
} }

View File

@ -19,6 +19,7 @@ import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.WindowManager import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.status.EditorBasedWidget import com.intellij.openapi.wm.impl.status.EditorBasedWidget
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.intellij.util.Consumer import com.intellij.util.Consumer
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.globalOptions import com.maddyhome.idea.vim.api.globalOptions
@ -67,6 +68,13 @@ internal object ShowCmd {
internal object ShowCmdOptionChangeListener : GlobalOptionChangeListener { internal object ShowCmdOptionChangeListener : GlobalOptionChangeListener {
override fun onGlobalOptionChanged() { override fun onGlobalOptionChanged() {
ShowCmd.update() ShowCmd.update()
val extension = StatusBarWidgetFactory.EP_NAME.findExtension(ShowCmdStatusBarWidgetFactory::class.java) ?: return
val projectManager = ProjectManager.getInstanceIfCreated() ?: return
for (project in projectManager.openProjects) {
val statusBarWidgetsManager = project.getService(StatusBarWidgetsManager::class.java) ?: continue
statusBarWidgetsManager.updateWidget(extension)
}
} }
} }

View File

@ -453,7 +453,7 @@ public class ExEntryPanel extends JPanel {
public static class LafListener implements LafManagerListener { public static class LafListener implements LafManagerListener {
@Override @Override
public void lookAndFeelChanged(@NotNull LafManager source) { public void lookAndFeelChanged(@NotNull LafManager source) {
if (VimPlugin.isNotEnabled()) return; if (!VimPlugin.isEnabled()) return;
// Calls updateUI on this and child components // Calls updateUI on this and child components
if (ExEntryPanel.isInstanceWithShortcutsActive()) { if (ExEntryPanel.isInstanceWithShortcutsActive()) {
IJSwingUtilities.updateComponentTreeUI(ExEntryPanel.getInstance()); IJSwingUtilities.updateComponentTreeUI(ExEntryPanel.getInstance());

View File

@ -1,31 +0,0 @@
/*
* 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.widgets
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.VimPluginListener
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
public class VimWidgetListener(private val updateWidget: Runnable) : GlobalOptionChangeListener, VimPluginListener {
init {
injector.listenersNotifier.vimPluginListeners.add(this)
}
override fun onGlobalOptionChanged() {
updateWidget.run()
}
override fun turnedOn() {
updateWidget.run()
}
override fun turnedOff() {
updateWidget.run()
}
}

View File

@ -1,94 +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.ui.widgets.macro
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.MacroRecordingListener
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
import com.maddyhome.idea.vim.ui.widgets.mode.VimStatusBarWidget
import java.awt.Component
private const val ID = "IdeaVim::Macro"
internal class MacroWidgetFactory : StatusBarWidgetFactory, VimStatusBarWidget {
private var content: String = ""
private val macroRecordingListener = object : MacroRecordingListener {
override fun recordingStarted(editor: VimEditor, register: Char) {
content = "recording @$register"
updateWidgetInStatusBar(ID, editor.ij.project)
}
override fun recordingFinished(editor: VimEditor, register: Char) {
content = ""
updateWidgetInStatusBar(ID, editor.ij.project)
}
}
override fun getId(): String {
return ID
}
override fun getDisplayName(): String {
return "IdeaVim Macro Recording Widget"
}
override fun createWidget(project: Project): StatusBarWidget {
injector.listenersNotifier.macroRecordingListeners.add(macroRecordingListener)
return VimMacroWidget()
}
override fun isAvailable(project: Project): Boolean {
return VimPlugin.isEnabled() && injector.globalIjOptions().showmodewidget
}
private inner class VimMacroWidget : StatusBarWidget {
override fun ID(): String {
return ID
}
override fun getPresentation(): StatusBarWidget.WidgetPresentation {
return VimModeWidgetPresentation()
}
}
private inner class VimModeWidgetPresentation : StatusBarWidget.TextPresentation {
override fun getAlignment(): Float = Component.CENTER_ALIGNMENT
override fun getText(): String {
return content
}
override fun getTooltipText(): String {
return content.ifEmpty {
"No macro recording in progress"
}
}
}
}
public fun updateMacroWidget() {
val factory = StatusBarWidgetFactory.EP_NAME.findExtension(MacroWidgetFactory::class.java) ?: return
for (project in ProjectManager.getInstance().openProjects) {
val statusBarWidgetsManager = project.service<StatusBarWidgetsManager>()
statusBarWidgetsManager.updateWidget(factory)
}
}
public val macroWidgetOptionListener: VimWidgetListener = VimWidgetListener { updateMacroWidget() }

View File

@ -1,45 +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.ui.widgets.mode
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.ui.widgets.VimWidgetListener
public class ModeWidgetFactory : StatusBarWidgetFactory {
public companion object {
public const val ID: String = "IdeaVim::Mode"
}
override fun getId(): String {
return ID
}
override fun getDisplayName(): String {
return "IdeaVim Mode Widget"
}
override fun createWidget(project: Project): StatusBarWidget {
return VimModeWidget(project)
}
override fun isAvailable(project: Project): Boolean {
return VimPlugin.isEnabled()
&& injector.globalIjOptions().showmodewidget
&& !project.isDisposed
&& FileEditorManager.getInstance(project).hasOpenFiles()
}
}
public val modeWidgetOptionListener: VimWidgetListener = VimWidgetListener { updateModeWidget() }

View File

@ -1,368 +0,0 @@
/*
* 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.widgets.mode
import com.intellij.ide.ui.LafManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.ui.DialogPanel
import com.intellij.openapi.ui.popup.JBPopup
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.components.JBTabbedPane
import com.intellij.ui.content.ContentFactory
import com.intellij.ui.dsl.builder.Cell
import com.intellij.ui.dsl.builder.RowLayout
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.bindItem
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.selected
import com.intellij.ui.dsl.builder.toNullableProperty
import com.intellij.ui.layout.not
import com.intellij.util.Alarm
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import com.maddyhome.idea.vim.vimscript.model.datatypes.asVimInt
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.FlowLayout
import javax.swing.BorderFactory
import javax.swing.JButton
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
public class ModeWidgetPopup : AnAction() {
public override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val popup = createPopup() ?: return
popup.showCenteredInCurrentWindow(project)
}
public companion object {
@Volatile
private var currentPopup: JBPopup? = null
public fun createPopup(): JBPopup? {
synchronized(this) {
if (currentPopup?.isDisposed == false) return null
val mainPanel = JPanel(BorderLayout())
val buttonPanel = JPanel(FlowLayout(FlowLayout.RIGHT))
val applyButton = JButton("Apply").apply { isEnabled = false }
val cancelButton = JButton("Close")
buttonPanel.add(applyButton)
buttonPanel.add(cancelButton)
mainPanel.add(buttonPanel, BorderLayout.SOUTH)
val tabbedPane = JBTabbedPane()
val lightThemeSettings = createPanel(getWidgetThemeColors(true))
val darkThemeSettings = createPanel(getWidgetThemeColors(false))
tabbedPane.addTab(MessageHelper.getMessage("widget.mode.popup.tab.light"), lightThemeSettings.addScrollPane())
tabbedPane.addTab(MessageHelper.getMessage("widget.mode.popup.tab.dark"), darkThemeSettings.addScrollPane())
tabbedPane.preferredSize = Dimension(300, 300)
for (i in 0 until tabbedPane.tabCount) {
val label = JLabel(tabbedPane.getTitleAt(i), JLabel.CENTER)
label.preferredSize = Dimension(126, tabbedPane.getTabComponentAt(i).preferredSize.height)
tabbedPane.setTabComponentAt(i, label)
}
tabbedPane.selectedIndex = if (LafManager.getInstance().currentUIThemeLookAndFeel.isDark) 1 else 0
mainPanel.add(tabbedPane, BorderLayout.CENTER)
val popupContent = ContentFactory.getInstance().createContent(mainPanel, "", false).component
val popup = JBPopupFactory.getInstance()
.createComponentPopupBuilder(popupContent, popupContent)
.setTitle(MessageHelper.getMessage("widget.mode.popup.title"))
.setResizable(true)
.setMovable(true)
.setRequestFocus(true)
.setCancelOnClickOutside(false)
.setCancelKeyEnabled(false)
.createPopup()
applyButton.addActionListener {
lightThemeSettings.apply()
darkThemeSettings.apply()
repaintModeWidget()
}
cancelButton.addActionListener {
popup.cancel()
}
val alarm = Alarm(popup)
fun updateApplyButtonVisibility() {
alarm.addRequest({
applyButton.isEnabled = lightThemeSettings.isModified() || darkThemeSettings.isModified()
updateApplyButtonVisibility()
}, 500L)
}
updateApplyButtonVisibility()
currentPopup = popup
return currentPopup
}
}
private fun getWidgetThemeColors(isLight: Boolean): ModeColors {
val keyPostfix = if (isLight) "_light" else "_dark"
return ModeColors(
"widget_mode_is_full_customization$keyPostfix",
"widget_mode_theme$keyPostfix",
"widget_mode_normal_background$keyPostfix",
"widget_mode_normal_foreground$keyPostfix",
"widget_mode_insert_background$keyPostfix",
"widget_mode_insert_foreground$keyPostfix",
"widget_mode_replace_background$keyPostfix",
"widget_mode_replace_foreground$keyPostfix",
"widget_mode_command_background$keyPostfix",
"widget_mode_command_foreground$keyPostfix",
"widget_mode_visual_background$keyPostfix",
"widget_mode_visual_foreground$keyPostfix",
"widget_mode_visual_line_background$keyPostfix",
"widget_mode_visual_line_foreground$keyPostfix",
"widget_mode_visual_block_background$keyPostfix",
"widget_mode_visual_block_foreground$keyPostfix",
"widget_mode_select_background$keyPostfix",
"widget_mode_select_foreground$keyPostfix",
"widget_mode_select_line_background$keyPostfix",
"widget_mode_select_line_foreground$keyPostfix",
"widget_mode_select_block_background$keyPostfix",
"widget_mode_select_block_foreground$keyPostfix",
)
}
private fun createPanel(modeColors: ModeColors): DialogPanel {
val panel = panel {
lateinit var advancedSettings: Cell<JBCheckBox>
row {
advancedSettings = checkBox(MessageHelper.getMessage("widget.mode.popup.field.advanced.settings")).bindSelected(modeColors::isFullCustomization)
}
group {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.theme"))
comboBox(ModeWidgetTheme.values().toList()).bindItem(modeColors::theme.toNullableProperty())
}
row { browserLink("Suggest your theme", "https://youtrack.jetbrains.com/issue/VIM-1377/Normal-mode-needs-to-be-more-obvious") }
}.topGap(TopGap.NONE).visibleIf(!advancedSettings.selected)
group(MessageHelper.getMessage("widget.mode.popup.group.title.full.customization")) {
row { text(MessageHelper.getMessage("widget.mode.popup.color.instruction")) }
group(MessageHelper.getMessage("widget.mode.popup.group.normal.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::normalBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::normalFg)
}.layout(RowLayout.PARENT_GRID)
}
group(MessageHelper.getMessage("widget.mode.popup.group.insert.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::insertBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::insertFg)
}.layout(RowLayout.PARENT_GRID)
}
group(MessageHelper.getMessage("widget.mode.popup.group.replace.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::replaceBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::replaceFg)
}.layout(RowLayout.PARENT_GRID)
}
group(MessageHelper.getMessage("widget.mode.popup.group.command.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::commandBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::commandFg)
}.layout(RowLayout.PARENT_GRID)
}
group(MessageHelper.getMessage("widget.mode.popup.group.visual.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::visualBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::visualFg)
}.layout(RowLayout.PARENT_GRID)
collapsibleGroup(MessageHelper.getMessage("widget.mode.popup.group.visual.subgroup.line.title")) {
row { text(MessageHelper.getMessage("widget.mode.popup.group.visual.subgroup.instruction")) }
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::visualLineBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::visualLineFg)
}.layout(RowLayout.PARENT_GRID)
}
collapsibleGroup(MessageHelper.getMessage("widget.mode.popup.group.visual.subgroup.block.title")) {
row { text(MessageHelper.getMessage("widget.mode.popup.group.visual.subgroup.instruction")) }
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::visualBlockBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::visualBlockFg)
}.layout(RowLayout.PARENT_GRID)
}
}
group(MessageHelper.getMessage("widget.mode.popup.group.select.title")) {
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::selectBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::selectFg)
}.layout(RowLayout.PARENT_GRID)
collapsibleGroup(MessageHelper.getMessage("widget.mode.popup.group.select.subgroup.line.title")) {
row { text(MessageHelper.getMessage("widget.mode.popup.group.select.subgroup.instruction")) }
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::selectLineBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::selectLineFg)
}.layout(RowLayout.PARENT_GRID)
}
collapsibleGroup(MessageHelper.getMessage("widget.mode.popup.group.select.subgroup.block.title")) {
row { text(MessageHelper.getMessage("widget.mode.popup.group.select.subgroup.instruction")) }
row {
label(MessageHelper.getMessage("widget.mode.popup.field.background"))
textField().bindText(modeColors::selectBlockBg)
}.layout(RowLayout.PARENT_GRID)
row {
label(MessageHelper.getMessage("widget.mode.popup.field.foreground"))
textField().bindText(modeColors::selectBlockFg)
}.layout(RowLayout.PARENT_GRID)
}
}
}.topGap(TopGap.NONE).visibleIf(advancedSettings.selected)
}
return panel
}
private fun JComponent.addScrollPane(): JComponent {
val scrollPane = JBScrollPane(this, JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
scrollPane.border = BorderFactory.createEmptyBorder()
return scrollPane
}
}
private class ModeColors(
isFullCustomizationKey: String, themeKey: String,
normalBgKey: String, normalFgKey: String,
insertBgKey: String, insertFgKey: String,
replaceBgKey: String, replaceFgKey: String,
commandBgKey: String, commandFgKey: String,
visualBgKey: String, visualFgKey: String, visualLineBgKey: String, visualLineFgKey: String, visualBlockBgKey: String, visualBlockFgKey: String,
selectBgKey: String, selectFgKey: String, selectLineBgKey: String, selectLineFgKey: String, selectBlockBgKey: String, selectBlockFgKey: String
) {
var isFullCustomization: Boolean by VimScopeBooleanVariable(isFullCustomizationKey)
var theme: ModeWidgetTheme by VimScopeThemeVariable(themeKey)
var normalBg: String by VimScopeStringVariable(normalBgKey)
var normalFg: String by VimScopeStringVariable(normalFgKey)
var insertBg: String by VimScopeStringVariable(insertBgKey)
var insertFg: String by VimScopeStringVariable(insertFgKey)
var replaceBg: String by VimScopeStringVariable(replaceBgKey)
var replaceFg: String by VimScopeStringVariable(replaceFgKey)
var commandBg: String by VimScopeStringVariable(commandBgKey)
var commandFg: String by VimScopeStringVariable(commandFgKey)
var visualBg: String by VimScopeStringVariable(visualBgKey)
var visualFg: String by VimScopeStringVariable(visualFgKey)
var visualLineBg: String by VimScopeStringVariable(visualLineBgKey)
var visualLineFg: String by VimScopeStringVariable(visualLineFgKey)
var visualBlockBg: String by VimScopeStringVariable(visualBlockBgKey)
var visualBlockFg: String by VimScopeStringVariable(visualBlockFgKey)
var selectBg: String by VimScopeStringVariable(selectBgKey)
var selectFg: String by VimScopeStringVariable(selectFgKey)
var selectLineBg: String by VimScopeStringVariable(selectLineBgKey)
var selectLineFg: String by VimScopeStringVariable(selectLineFgKey)
var selectBlockBg: String by VimScopeStringVariable(selectBlockBgKey)
var selectBlockFg: String by VimScopeStringVariable(selectBlockFgKey)
private class VimScopeBooleanVariable(private var key: String): ReadWriteProperty<ModeColors, Boolean> {
override fun getValue(thisRef: ModeColors, property: KProperty<*>): Boolean {
return injector.variableService.getVimVariable(key)?.asBoolean() ?: false
}
override fun setValue(thisRef: ModeColors, property: KProperty<*>, value: Boolean) {
injector.variableService.storeVimVariable(key, value.asVimInt())
}
}
private class VimScopeStringVariable(private var key: String): ReadWriteProperty<ModeColors, String> {
override fun getValue(thisRef: ModeColors, property: KProperty<*>): String {
return injector.variableService.getVimVariable(key)?.asString() ?: ""
}
override fun setValue(thisRef: ModeColors, property: KProperty<*>, value: String) {
injector.variableService.storeVimVariable(key, VimString(value))
}
}
private class VimScopeThemeVariable(private var key: String): ReadWriteProperty<ModeColors, ModeWidgetTheme> {
override fun getValue(thisRef: ModeColors, property: KProperty<*>): ModeWidgetTheme {
val themeString = injector.variableService.getVimVariable(key)?.asString() ?: return ModeWidgetTheme.getDefaultTheme()
return ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
}
override fun setValue(thisRef: ModeColors, property: KProperty<*>, value: ModeWidgetTheme) {
injector.variableService.storeVimVariable(key, VimString(value.toString()))
}
}
}
}
public enum class ModeWidgetTheme(private var value: String) {
TEST("Nord-Aurora (testing, will be removed)"),
COLORLESS("Colorless");
override fun toString(): String {
return value
}
public companion object {
public fun parseString(string: String): ModeWidgetTheme? {
return ModeWidgetTheme.values().firstOrNull { it.value == string }
}
public fun getDefaultTheme(): ModeWidgetTheme = TEST
}
}

View File

@ -1,129 +0,0 @@
/*
* 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.widgets.mode
import com.intellij.ide.ui.LafManager
import com.intellij.util.ui.UIUtil
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import java.awt.Color
public fun getModeBackground(mode: Mode?): Color {
val isLight = !LafManager.getInstance().currentUIThemeLookAndFeel.isDark
val keyPostfix = if (isLight) "_light" else "_dark"
if (injector.variableService.getVimVariable("widget_mode_is_full_customization$keyPostfix")?.asBoolean() != true) {
val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: ""
val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
when (theme) {
ModeWidgetTheme.TEST -> {
return when (mode) {
Mode.INSERT -> Color.decode("#D08770")
Mode.REPLACE -> Color.decode("#EBCB8B")
is Mode.NORMAL -> Color.decode("#BF616A")
is Mode.CMD_LINE -> Color.decode("#A3BE8C")
is Mode.VISUAL -> Color.decode("#B48EAD")
is Mode.SELECT -> Color.decode("#B48EAD")
is Mode.OP_PENDING, null -> UIUtil.getPanelBackground()
}
}
ModeWidgetTheme.COLORLESS -> {
return UIUtil.getPanelBackground()
}
}
} else {
val colorString = when (mode) {
Mode.INSERT -> injector.variableService.getVimVariable("widget_mode_insert_background$keyPostfix")
Mode.REPLACE -> injector.variableService.getVimVariable("widget_mode_replace_background$keyPostfix")
is Mode.NORMAL -> injector.variableService.getVimVariable("widget_mode_normal_background$keyPostfix")
is Mode.CMD_LINE -> injector.variableService.getVimVariable("widget_mode_command_background$keyPostfix")
is Mode.VISUAL -> {
val visualModeBackground = injector.variableService.getVimVariable("widget_mode_visual_background$keyPostfix")
when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> visualModeBackground
SelectionType.LINE_WISE -> injector.variableService.getVimVariable("widget_mode_visual_line_background$keyPostfix") ?: visualModeBackground
SelectionType.BLOCK_WISE -> injector.variableService.getVimVariable("widget_mode_visual_block_background$keyPostfix") ?: visualModeBackground
}
}
is Mode.SELECT -> {
val selectModeBackground = injector.variableService.getVimVariable("widget_mode_select_background$keyPostfix")
when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> selectModeBackground
SelectionType.LINE_WISE -> injector.variableService.getVimVariable("widget_mode_select_line_background$keyPostfix") ?: selectModeBackground
SelectionType.BLOCK_WISE -> injector.variableService.getVimVariable("widget_mode_select_block_background$keyPostfix") ?: selectModeBackground
}
}
is Mode.OP_PENDING, null -> null
}?.asString()
val defaultColor = UIUtil.getPanelBackground()
val color = when (colorString) {
"v:status_bar_bg" -> UIUtil.getPanelBackground()
"v:status_bar_fg" -> UIUtil.getLabelForeground()
else -> {
if (colorString == null) {
defaultColor
} else {
try { Color.decode(colorString) } catch (e: Exception) { defaultColor }
}
}
}
return color
}
}
public fun getModeForeground(mode: Mode?): Color {
val isLight = !LafManager.getInstance().currentUIThemeLookAndFeel.isDark
val keyPostfix = if (isLight) "_light" else "_dark"
if (injector.variableService.getVimVariable("widget_mode_is_full_customization$keyPostfix")?.asBoolean() != true) {
val themeString = injector.variableService.getVimVariable("widget_mode_theme$keyPostfix")?.asString() ?: ""
val theme = ModeWidgetTheme.parseString(themeString) ?: ModeWidgetTheme.getDefaultTheme()
return when (theme) {
ModeWidgetTheme.TEST -> Color.decode("#2E3440")
ModeWidgetTheme.COLORLESS -> UIUtil.getLabelForeground()
}
} else {
val colorString = when (mode) {
Mode.INSERT -> injector.variableService.getVimVariable("widget_mode_insert_foreground$keyPostfix")
Mode.REPLACE -> injector.variableService.getVimVariable("widget_mode_replace_foreground$keyPostfix")
is Mode.NORMAL -> injector.variableService.getVimVariable("widget_mode_normal_foreground$keyPostfix")
is Mode.CMD_LINE -> injector.variableService.getVimVariable("widget_mode_command_foreground$keyPostfix")
is Mode.VISUAL -> {
val visualModeBackground = injector.variableService.getVimVariable("widget_mode_visual_foreground$keyPostfix")
when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> visualModeBackground
SelectionType.LINE_WISE -> injector.variableService.getVimVariable("widget_mode_visual_line_foreground$keyPostfix") ?: visualModeBackground
SelectionType.BLOCK_WISE -> injector.variableService.getVimVariable("widget_mode_visual_block_foreground$keyPostfix") ?: visualModeBackground
}
}
is Mode.SELECT -> {
val selectModeBackground = injector.variableService.getVimVariable("widget_mode_select_foreground$keyPostfix")
when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> selectModeBackground
SelectionType.LINE_WISE -> injector.variableService.getVimVariable("widget_mode_select_line_foreground$keyPostfix") ?: selectModeBackground
SelectionType.BLOCK_WISE -> injector.variableService.getVimVariable("widget_mode_select_block_foreground$keyPostfix") ?: selectModeBackground
}
}
is Mode.OP_PENDING, null -> null
}?.asString()
val defaultColor = UIUtil.getLabelForeground()
val color = when (colorString) {
"v:status_bar_bg" -> UIUtil.getPanelBackground()
"v:status_bar_fg" -> UIUtil.getLabelForeground()
else -> {
if (colorString == null) {
defaultColor
} else {
try { Color.decode(colorString) } catch (e: Exception) { defaultColor }
}
}
}
return color
}
}

View File

@ -1,176 +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.ui.widgets.mode
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.wm.CustomStatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
import com.intellij.ui.awt.RelativePoint
import com.intellij.ui.components.JBLabel
import com.intellij.ui.util.width
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetFocusListener
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetModeListener
import java.awt.Dimension
import java.awt.Point
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.JComponent
import kotlin.math.max
public class VimModeWidget(public val project: Project) : CustomStatusBarWidget, VimStatusBarWidget {
private companion object {
private const val INSERT = "INSERT"
private const val NORMAL = "NORMAL"
private const val REPLACE = "REPLACE"
private const val COMMAND = "COMMAND"
private const val VISUAL = "VISUAL"
private const val VISUAL_LINE = "V-LINE"
private const val VISUAL_BLOCK = "V-BLOCK"
private const val SELECT = "SELECT"
private const val SELECT_LINE = "S-LINE"
private const val SELECT_BLOCK = "S-BLOCK"
}
private val useColors = injector.globalIjOptions().colorfulmodewidget
private val label = JBLabelWiderThan(setOf(REPLACE)).apply {
isOpaque = useColors
}
init {
val mode = getFocusedEditor(project)?.vim?.mode
updateLabel(mode)
injector.listenersNotifier.apply {
modeChangeListeners.add(ModeWidgetModeListener(this@VimModeWidget))
myEditorListeners.add(ModeWidgetFocusListener(this@VimModeWidget))
}
label.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
val popup = ModeWidgetPopup.createPopup() ?: return
val dimension = popup.content.preferredSize
val widgetLocation = e.component.locationOnScreen
popup.show(RelativePoint(Point(
widgetLocation.x + e.component.width - dimension.width,
widgetLocation.y - dimension.height,
)))
}
})
}
override fun ID(): String {
return ModeWidgetFactory.ID
}
override fun getComponent(): JComponent {
return label
}
public fun updateWidget() {
val mode = getFocusedEditor(project)?.vim?.mode
updateWidget(mode)
}
public fun updateWidget(mode: Mode?) {
updateLabel(mode)
updateWidgetInStatusBar(ModeWidgetFactory.ID, project)
}
private fun updateLabel(mode: Mode?) {
label.text = getModeText(mode)
if (useColors) {
label.foreground = getModeForeground(mode)
label.background = getModeBackground(mode)
}
}
private fun getFocusedEditor(project: Project): Editor? {
val fileEditorManager = FileEditorManager.getInstance(project)
return fileEditorManager.selectedTextEditor
}
private fun getModeText(mode: Mode?): String? {
return when (mode) {
Mode.INSERT -> INSERT
Mode.REPLACE -> REPLACE
is Mode.NORMAL -> NORMAL
is Mode.CMD_LINE -> COMMAND
is Mode.VISUAL -> getVisualModeText(mode)
is Mode.SELECT -> getSelectModeText(mode)
is Mode.OP_PENDING, null -> null
}
}
private fun getVisualModeText(mode: Mode.VISUAL) = when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> VISUAL
SelectionType.LINE_WISE -> VISUAL_LINE
SelectionType.BLOCK_WISE -> VISUAL_BLOCK
}
private fun getSelectModeText(mode: Mode.SELECT) = when (mode.selectionType) {
SelectionType.CHARACTER_WISE -> SELECT
SelectionType.LINE_WISE -> SELECT_LINE
SelectionType.BLOCK_WISE -> SELECT_BLOCK
}
private class JBLabelWiderThan(private val words: Collection<String>): JBLabel("", CENTER) {
private val wordWidth: Int
get() {
val fontMetrics = getFontMetrics(font)
return words.maxOfOrNull { fontMetrics.stringWidth(it) } ?: 0
}
override fun getMinimumSize(): Dimension {
val minimumSize = super.getMinimumSize()
return Dimension(max(minimumSize.width, wordWidth + insets.width), minimumSize.height)
}
override fun getPreferredSize(): Dimension {
val preferredSize = super.getPreferredSize()
return Dimension(max(preferredSize.width, wordWidth + insets.width), preferredSize.height)
}
override fun getMaximumSize(): Dimension {
val maximumSize = super.getMaximumSize()
return Dimension(max(maximumSize.width, wordWidth + insets.width), maximumSize.height)
}
}
}
public fun updateModeWidget() {
val factory = StatusBarWidgetFactory.EP_NAME.findExtension(ModeWidgetFactory::class.java) ?: return
for (project in ProjectManager.getInstance().openProjects) {
val statusBarWidgetsManager = project.service<StatusBarWidgetsManager>()
statusBarWidgetsManager.updateWidget(factory)
}
}
public fun repaintModeWidget() {
for (project in ProjectManager.getInstance().openProjects) {
val widgets = WindowManager.getInstance()?.getStatusBar(project)?.allWidgets ?: continue
for (widget in widgets) {
if (widget is VimModeWidget) {
widget.updateWidget()
}
}
}
}

View File

@ -1,59 +0,0 @@
/*
* 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.widgets.mode
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.WindowManager
import java.util.*
public interface VimStatusBarWidget {
public fun updateWidgetInStatusBar(widgetID: String, project: Project?) {
if (project == null) return
val windowManager = WindowManager.getInstance()
windowManager.getStatusBar(project)?.updateWidget(widgetID) ?: run {
TimerWithRetriesTask(500L, 50) {
val statusBar = windowManager.getStatusBar(project) ?: return@TimerWithRetriesTask false
statusBar.updateWidget(widgetID)
return@TimerWithRetriesTask true
}.execute()
}
}
}
/**
* A task that may be used to address issues with initialization in the Platform, executing code with a reasonable number of retries and a reasonable period.
* Clearly, this is a workaround and its use should be avoided when possible.
*
* Why is it needed for widgets?
* In a single project environment, it is not necessary since the status bar is initialized before the editor opens.
* However, in multi-project setups, the editor window is opened before the status bar initialization.
* And this tasks tries to loops until status bar creation in order to notify it about opened editor.
*/
private class TimerWithRetriesTask(
private val period: Long,
private val retriesLimit: Int,
private val block: () -> Boolean,
) {
private val timer = Timer()
fun execute() {
timer.schedule(object : TimerTask() {
private var counter = 0
override fun run() {
if (counter >= retriesLimit) {
timer.cancel()
} else {
if (this@TimerWithRetriesTask.block()) timer.cancel()
counter++
}
}
}, 0L, period)
}
}

View File

@ -1,53 +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.ui.widgets.mode.listeners
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.common.EditorListener
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.ui.widgets.mode.VimModeWidget
import com.maddyhome.idea.vim.ui.widgets.mode.updateModeWidget
internal class ModeWidgetFocusListener(private val modeWidget: VimModeWidget): EditorListener {
override fun created(editor: VimEditor) {
updateModeWidget()
val mode = getFocusedEditorForProject(editor.ij.project)?.vim?.mode
modeWidget.updateWidget(mode)
}
override fun released(editor: VimEditor) {
updateModeWidget()
val focusedEditor = getFocusedEditorForProject(editor.ij.project)
if (focusedEditor == null || focusedEditor == editor.ij) {
modeWidget.updateWidget(null)
}
}
override fun focusGained(editor: VimEditor) {
if (editor.ij.project != modeWidget.project) return
val mode = editor.mode
modeWidget.updateWidget(mode)
}
override fun focusLost(editor: VimEditor) {
val mode = getFocusedEditorForProject(editor.ij.project)?.vim?.mode
modeWidget.updateWidget(mode)
}
private fun getFocusedEditorForProject(editorProject: Project?): Editor? {
if (editorProject != modeWidget.project) return null
val fileEditorManager = FileEditorManager.getInstance(editorProject)
return fileEditorManager.selectedTextEditor
}
}

View File

@ -1,25 +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.ui.widgets.mode.listeners
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.common.ModeChangeListener
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.ui.widgets.mode.VimModeWidget
internal class ModeWidgetModeListener(private val modeWidget: VimModeWidget): ModeChangeListener {
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
val editorMode = editor.mode
if (editorMode !is Mode.OP_PENDING && editor.ij.project == modeWidget.project) {
modeWidget.updateWidget(editorMode)
}
}
}

View File

@ -36,28 +36,31 @@ public val VimEditor.isIdeaRefactorModeSelect: Boolean
internal object IdeaRefactorModeHelper { internal object IdeaRefactorModeHelper {
sealed interface Action { fun correctSelection(editor: Editor) {
object RemoveSelection : Action val action: () -> Unit = {
class SetMode(val newMode: Mode) : Action val mode = editor.vim.mode
class MoveToOffset(val newOffset: Int) : Action if (!mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
}
fun applyCorrections(corrections: List<Action>, editor: Editor) {
val correctionsApplier = {
corrections.forEach { correction ->
when (correction) {
is Action.MoveToOffset -> {
editor.caretModel.moveToOffset(correction.newOffset)
}
Action.RemoveSelection -> {
SelectionVimListenerSuppressor.lock().use { SelectionVimListenerSuppressor.lock().use {
editor.selectionModel.removeSelection() editor.selectionModel.removeSelection()
} }
} }
if (mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
val autodetectedSubmode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim)
if (mode.selectionType != autodetectedSubmode) {
// Update the submode
val newMode = when (mode) {
is Mode.SELECT -> mode.copy(selectionType = autodetectedSubmode)
is Mode.VISUAL -> mode.copy(selectionType = autodetectedSubmode)
else -> error("IdeaVim should be either in visual or select modes")
}
editor.vim.vimStateMachine.mode = newMode
}
}
is Action.SetMode -> { if (editor.hasBlockOrUnderscoreCaret()) {
editor.vim.vimStateMachine.mode = correction.newMode TemplateManagerImpl.getTemplateState(editor)?.currentVariableRange?.let { segmentRange ->
if (!segmentRange.isEmpty && segmentRange.endOffset == editor.caretModel.offset && editor.caretModel.offset != 0) {
editor.caretModel.moveToOffset(editor.caretModel.offset - 1)
} }
} }
} }
@ -67,9 +70,7 @@ internal object IdeaRefactorModeHelper {
if (lookup != null) { if (lookup != null) {
val selStart = editor.selectionModel.selectionStart val selStart = editor.selectionModel.selectionStart
val selEnd = editor.selectionModel.selectionEnd val selEnd = editor.selectionModel.selectionEnd
lookup.performGuardedChange { lookup.performGuardedChange(action)
correctionsApplier()
}
lookup.addLookupListener(object : LookupListener { lookup.addLookupListener(object : LookupListener {
override fun beforeItemSelected(event: LookupEvent): Boolean { override fun beforeItemSelected(event: LookupEvent): Boolean {
// FIXME: 01.11.2019 Nasty workaround because of problems in IJ platform // FIXME: 01.11.2019 Nasty workaround because of problems in IJ platform
@ -81,41 +82,7 @@ internal object IdeaRefactorModeHelper {
} }
}) })
} else { } else {
correctionsApplier() action()
}
}
fun calculateCorrections(editor: Editor): List<Action> {
val corrections = mutableListOf<Action>()
val mode = editor.vim.mode
if (!mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
corrections.add(Action.RemoveSelection)
}
if (mode.hasVisualSelection && editor.selectionModel.hasSelection()) {
val autodetectedSubmode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim)
if (mode.selectionType != autodetectedSubmode) {
// Update the submode
val newMode = when (mode) {
is Mode.SELECT -> mode.copy(selectionType = autodetectedSubmode)
is Mode.VISUAL -> mode.copy(selectionType = autodetectedSubmode)
else -> error("IdeaVim should be either in visual or select modes")
}
corrections.add(Action.SetMode(newMode))
}
}
if (editor.hasBlockOrUnderscoreCaret()) {
TemplateManagerImpl.getTemplateState(editor)?.currentVariableRange?.let { segmentRange ->
if (!segmentRange.isEmpty && segmentRange.endOffset == editor.caretModel.offset && editor.caretModel.offset != 0) {
corrections.add(Action.MoveToOffset(editor.caretModel.offset - 1))
} }
} }
} }
return corrections
}
fun correctSelection(editor: Editor) {
val corrections = calculateCorrections(editor)
applyCorrections(corrections, editor)
}
}

View File

@ -8,10 +8,6 @@
package com.maddyhome.idea.vim.vimscript.services package com.maddyhome.idea.vim.vimscript.services
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment
@ -26,10 +22,8 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimList
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
import com.maddyhome.idea.vim.vimscript.model.expressions.Variable import com.maddyhome.idea.vim.vimscript.model.expressions.Variable
import org.jdom.Element
@State(name = "VimVariables", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)]) internal class IjVariableService : VimVariableServiceBase() {
internal class IjVariableService : VimVariableServiceBase(), PersistentStateComponent<Element?> {
override fun storeVariable(variable: Variable, value: VimDataType, editor: VimEditor, context: ExecutionContext, vimContext: VimLContext) { override fun storeVariable(variable: Variable, value: VimDataType, editor: VimEditor, context: ExecutionContext, vimContext: VimLContext) {
super.storeVariable(variable, value, editor, context, vimContext) super.storeVariable(variable, value, editor, context, vimContext)
@ -53,49 +47,4 @@ internal class IjVariableService : VimVariableServiceBase(), PersistentStateComp
else -> error("Unexpected") else -> error("Unexpected")
} }
} }
override fun getState(): Element {
val element = Element("variables")
saveData(element)
return element
}
override fun loadState(state: Element) {
readData(state)
}
private fun saveData(element: Element) {
val vimVariablesElement = Element("vim-variables")
for ((key, value) in vimVariables.entries) {
if (value is VimString) {
val variableElement = Element("variable")
variableElement.setAttribute("key", key)
variableElement.setAttribute("value", value.value)
variableElement.setAttribute("type", "string")
vimVariablesElement.addContent(variableElement)
} else if (value is VimInt) {
val variableElement = Element("variable")
variableElement.setAttribute("key", key)
variableElement.setAttribute("value", value.value.toString())
variableElement.setAttribute("type", "int")
vimVariablesElement.addContent(variableElement)
}
}
element.addContent(vimVariablesElement)
}
private fun readData(element: Element) {
val vimVariablesElement = element.getChild("vim-variables")
val variableElements = vimVariablesElement.getChildren("variable")
for (variableElement in variableElements) {
when (variableElement.getAttributeValue("type")) {
"string" -> {
vimVariables[variableElement.getAttributeValue("key")] = VimString(variableElement.getAttributeValue("value"))
}
"int" -> {
vimVariables[variableElement.getAttributeValue("key")] = VimInt(variableElement.getAttributeValue("value"))
}
}
}
}
} }

View File

@ -1,19 +0,0 @@
<!--
~ 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.
-->
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<editorActionHandler action="EditorEscape"
implementationClass="com.maddyhome.idea.vim.handler.VimEscForRiderHandler"
id="ideavim-clion-nova-esc"
order="first, before idea.only.escape"/>
</extensions>
<extensions defaultExtensionNs="IdeaVIM">
<clionNovaProvider implementation="com.maddyhome.idea.vim.ide.ClionNovaProviderImpl"/>
</extensions>
</idea-plugin>

View File

@ -29,8 +29,6 @@
<!--suppress PluginXmlValidity --> <!--suppress PluginXmlValidity -->
<depends optional="true" config-file="ides/ideavim-withRider.xml">com.intellij.modules.rider</depends> <depends optional="true" config-file="ides/ideavim-withRider.xml">com.intellij.modules.rider</depends>
<!--suppress PluginXmlValidity --> <!--suppress PluginXmlValidity -->
<depends optional="true" config-file="ides/ideavim-withClionNova.xml">org.jetbrains.plugins.clion.radler</depends>
<!--suppress PluginXmlValidity -->
<depends optional="true" config-file="ides/ideavim-withAppCode.xml">com.intellij.modules.appcode</depends> <depends optional="true" config-file="ides/ideavim-withAppCode.xml">com.intellij.modules.appcode</depends>
<depends optional="true" config-file="ideavim-withAceJump.xml">AceJump</depends> <depends optional="true" config-file="ideavim-withAceJump.xml">AceJump</depends>
@ -57,8 +55,6 @@
<extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true"> <extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true">
<with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/> <with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/>
</extensionPoint> </extensionPoint>
<extensionPoint interface="com.maddyhome.idea.vim.ide.ClionNovaProvider" dynamic="true" name="clionNovaProvider"/>
</extensionPoints> </extensionPoints>
<extensions defaultExtensionNs="com.intellij"> <extensions defaultExtensionNs="com.intellij">
@ -66,9 +62,7 @@
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/> <projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
<projectService serviceImplementation="com.maddyhome.idea.vim.group.LastTabService"/> <projectService serviceImplementation="com.maddyhome.idea.vim.group.LastTabService"/>
<statusBarWidgetFactory id="IdeaVim-Icon" implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/> <statusBarWidgetFactory id="IdeaVim-Icon" implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/>
<statusBarWidgetFactory id="IdeaVim::Mode" implementation="com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetFactory" order="last"/>
<statusBarWidgetFactory id="IdeaVim::ShowCmd" implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/> <statusBarWidgetFactory id="IdeaVim::ShowCmd" implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/>
<statusBarWidgetFactory id="IdeaVim::Macro" implementation="com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetFactory"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/> <applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
@ -152,7 +146,6 @@
<action id="VimShortcutKeyAction" class="com.maddyhome.idea.vim.action.VimShortcutKeyAction"/> <action id="VimShortcutKeyAction" class="com.maddyhome.idea.vim.action.VimShortcutKeyAction"/>
<action id="VimActions" class="com.maddyhome.idea.vim.ui.VimActions"/> <action id="VimActions" class="com.maddyhome.idea.vim.ui.VimActions"/>
<action id="CustomizeModeWidget" class="com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetPopup"/>
<group id="IdeaVim.ReloadVimRc.group" class="com.maddyhome.idea.vim.ui.ReloadFloatingToolbarActionGroup"> <group id="IdeaVim.ReloadVimRc.group" class="com.maddyhome.idea.vim.ui.ReloadFloatingToolbarActionGroup">
<action id="IdeaVim.ReloadVimRc.reload" class="com.maddyhome.idea.vim.ui.ReloadVimRc" <action id="IdeaVim.ReloadVimRc.reload" class="com.maddyhome.idea.vim.ui.ReloadVimRc"

View File

@ -84,8 +84,6 @@ action.VimShortcutKeyAction.text=Shortcuts
action.VimActions.text=Vim Actions action.VimActions.text=Vim Actions
action.not.found.0=Action not found: {0} action.not.found.0=Action not found: {0}
action.CustomizeModeWidget.text=Mode Widget Settings
action.VimFindActionIdAction.text=IdeaVim: Track Action Ids action.VimFindActionIdAction.text=IdeaVim: Track Action Ids
action.VimFindActionIdAction.description=Starts tracking ids of executed actions action.VimFindActionIdAction.description=Starts tracking ids of executed actions
@ -131,28 +129,6 @@ action.finish.eap.text=Finish EAP
# Don't forget to update README if you modify this entry # Don't forget to update README if you modify this entry
action.subscribe.to.eap.text=Subscribe to EAP action.subscribe.to.eap.text=Subscribe to EAP
widget.mode.popup.title=Mode Widget Colors
widget.mode.popup.tab.light=Light Theme
widget.mode.popup.tab.dark=Dark Theme
widget.mode.popup.color.instruction=Use HEX color values for exact colors; use v:status_bar_bg to use your IDE's status bar background color and v:status_bar_fg for the foreground
widget.mode.popup.field.theme=Widget theme:
widget.mode.popup.field.advanced.settings=Full color customization (advanced)
widget.mode.popup.group.title.full.customization=Full customization
widget.mode.popup.group.normal.title=Normal Mode
widget.mode.popup.group.insert.title=Insert Mode
widget.mode.popup.group.replace.title=Replace Mode
widget.mode.popup.group.command.title=Command Mode
widget.mode.popup.group.visual.title=Visual Mode
widget.mode.popup.group.visual.subgroup.instruction=Leave fields empty to inherit colors from Visual Mode
widget.mode.popup.group.visual.subgroup.line.title=Visual Line
widget.mode.popup.group.visual.subgroup.block.title=Visual Block
widget.mode.popup.group.select.title=Select Mode
widget.mode.popup.group.select.subgroup.instruction=Leave fields empty to inherit colors from Select Mode
widget.mode.popup.group.select.subgroup.line.title=Select Line
widget.mode.popup.group.select.subgroup.block.title=Select Block
widget.mode.popup.field.background=Background:
widget.mode.popup.field.foreground=Text:
configurable.name.vim.emulation=Vim configurable.name.vim.emulation=Vim
configurable.keyhandler.link=<html>Use <a>sethandler</a> command to configure handlers from the .ideavimrc file</html> configurable.keyhandler.link=<html>Use <a>sethandler</a> command to configure handlers from the .ideavimrc file</html>
configurable.noneditablehandler.helper.text.with.example=Non-editable handlers are defined in .ideavimrc file. E.g. ''{0}'' for {1}. configurable.noneditablehandler.helper.text.with.example=Non-editable handlers are defined in .ideavimrc file. E.g. ''{0}'' for {1}.

View File

@ -1,37 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.ideavim
import com.intellij.testFramework.LoggedErrorProcessor
import com.intellij.testFramework.TestLoggerFactory.TestLoggerAssertionError
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.fail
/**
* By default, LOG.error does three things in tests:
* - rethrows the exception
* - logs error
* - prints to stderr
*
* The problem is that if we catch exception in tests, such an approach will print the exception to stderr and it will
* look like the exception is not processed.
* I don't see a need for printing these caught exceptions, so we can use this processor to only rethrow them.
*/
internal object OnlyThrowLoggedErrorProcessor : LoggedErrorProcessor() {
override fun processError(category: String, message: String, details: Array<out String>, t: Throwable?): Set<Action> {
return setOf(Action.RETHROW)
}
}
/**
* Asserts that [T] was thrown via `LOG.error("message", e)` call where `e` has a type of [T].
*/
internal inline fun <reified T: Throwable> assertThrowsLogError(crossinline action: () -> Unit): T {
val exception = assertThrows<TestLoggerAssertionError> {
LoggedErrorProcessor.executeWith<Throwable>(OnlyThrowLoggedErrorProcessor) {
action()
}
}
val cause = exception.cause
if (cause !is T) fail("Expected ${T::class.java} exception in LOG.error, but got $cause")
return cause
}

View File

@ -14,17 +14,12 @@ import com.intellij.openapi.editor.LogicalPosition
import com.intellij.testFramework.EditorTestUtil import com.intellij.testFramework.EditorTestUtil
import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.util.containers.toArray import com.intellij.util.containers.toArray
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.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.ExtensionHandler import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.key.MappingOwner
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
import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments
import kotlin.test.fail import kotlin.test.fail
@ -134,15 +129,3 @@ internal fun <T> product(vararg elements: List<T>): List<List<T>> {
} }
return res return res
} }
internal class ExceptionHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
error(exceptionMessage)
}
companion object {
internal const val exceptionMessage = "Exception here"
}
}
internal val exceptionMappingOwner = MappingOwner.Plugin.get("Exception mapping owner")

View File

@ -123,7 +123,7 @@ abstract class VimTestCase {
VimPlugin.getOptionGroup().resetAllOptionsForTesting() VimPlugin.getOptionGroup().resetAllOptionsForTesting()
VimPlugin.getKey().resetKeyMappings() VimPlugin.getKey().resetKeyMappings()
VimPlugin.getSearch().resetState() VimPlugin.getSearch().resetState()
if (VimPlugin.isNotEnabled()) VimPlugin.setEnabled(true) if (!VimPlugin.isEnabled()) VimPlugin.setEnabled(true)
injector.globalOptions().ideastrictmode = true injector.globalOptions().ideastrictmode = true
Checks.reset() Checks.reset()
clearClipboard() clearClipboard()
@ -152,8 +152,8 @@ abstract class VimTestCase {
@AfterEach @AfterEach
open fun tearDown(testInfo: TestInfo) { open fun tearDown(testInfo: TestInfo) {
val swingTimer = swingTimer
swingTimer?.stop() swingTimer?.stop()
swingTimer = null
val bookmarksManager = BookmarksManager.getInstance(fixture.project) val bookmarksManager = BookmarksManager.getInstance(fixture.project)
bookmarksManager?.bookmarks?.forEach { bookmark -> bookmarksManager?.bookmarks?.forEach { bookmark ->
bookmarksManager.remove(bookmark) bookmarksManager.remove(bookmark)
@ -170,7 +170,6 @@ abstract class VimTestCase {
injector.jumpService.resetJumps() injector.jumpService.resetJumps()
VimPlugin.getChange().resetRepeat() VimPlugin.getChange().resetRepeat()
VimPlugin.getKey().savedShortcutConflicts.clear() VimPlugin.getKey().savedShortcutConflicts.clear()
assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
// Tear down neovim // Tear down neovim
NeovimTesting.tearDown(testInfo) NeovimTesting.tearDown(testInfo)

View File

@ -7,40 +7,22 @@
*/ */
package org.jetbrains.plugins.ideavim.action package org.jetbrains.plugins.ideavim.action
import com.intellij.idea.TestFor
import com.intellij.testFramework.LoggedErrorProcessor
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.api.keys
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.helper.vimStateMachine import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.ExceptionHandler
import org.jetbrains.plugins.ideavim.OnlyThrowLoggedErrorProcessor
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
import org.jetbrains.plugins.ideavim.exceptionMappingOwner
import org.jetbrains.plugins.ideavim.rangeOf import org.jetbrains.plugins.ideavim.rangeOf
import org.jetbrains.plugins.ideavim.waitAndAssert import org.jetbrains.plugins.ideavim.waitAndAssert
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue
/** /**
* @author vlan * @author vlan
*/ */
class MacroActionTest : VimTestCase() { class MacroActionTest : VimTestCase() {
@AfterEach
fun tearDown() {
injector.keyGroup.removeKeyMapping(exceptionMappingOwner)
}
// |q| // |q|
@Test @Test
fun testRecordMacro() { fun testRecordMacro() {
@ -196,33 +178,4 @@ class MacroActionTest : VimTestCase() {
""".trimIndent(), """.trimIndent(),
) )
} }
@TestFor(issues = ["VIM-2929"])
@TestWithoutNeovim(reason = SkipNeovimReason.ACTION_COMMAND)
@Test
fun `macro to handler with exception`() {
configureByText(
"""
Lorem Ipsum
Lorem ipsum dolor sit amet,
${c}consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
injector.keyGroup.putKeyMapping(MappingMode.NXO, keys("abc"), exceptionMappingOwner, ExceptionHandler(), false)
injector.registerGroup.storeText('k', "abc")
injector.registerGroup.storeText('q', "x@ky")
val exception = assertThrows<Throwable> {
LoggedErrorProcessor.executeWith<Throwable>(OnlyThrowLoggedErrorProcessor) {
typeText("@q")
}
}
assertEquals(ExceptionHandler.exceptionMessage, exception.cause!!.cause!!.message)
assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
}
} }

View File

@ -1025,10 +1025,10 @@ $c tw${c}o
) )
assertState( assertState(
""" """
${s}one two <selection>one two
three four three four
five six five six
$se </selection>
""".trimIndent(), """.trimIndent(),
) )
} }

View File

@ -12,9 +12,8 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
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.common.Direction
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.common.Direction
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
@ -86,7 +85,7 @@ class GnNextTextObjectTest : VimTestCase() {
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
configureByText(before) configureByText(before)
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
typeText(keys) typeText(keys)
assertState(after) assertState(after)
assertState(Mode.NORMAL()) assertState(Mode.NORMAL())

View File

@ -12,9 +12,8 @@ package org.jetbrains.plugins.ideavim.action.motion.gn
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.common.Direction
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.common.Direction
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
@ -64,7 +63,7 @@ class GnPreviousTextObjectTest : VimTestCase() {
private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) { private fun doTestWithSearch(keys: List<KeyStroke>, before: String, after: String) {
configureByText(before) configureByText(before)
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
typeText(keys) typeText(keys)
assertState(after) assertState(after)
assertState(Mode.NORMAL()) assertState(Mode.NORMAL())

View File

@ -11,10 +11,9 @@ import com.intellij.idea.TestFor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction 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.Direction
import com.maddyhome.idea.vim.newapi.vim
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.common.Direction
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
@ -58,7 +57,7 @@ class VisualSelectNextSearchTest : VimTestCase() {
@Test @Test
fun testWithoutSpaces() { fun testWithoutSpaces() {
configureByText("test<caret>test") configureByText("test<caret>test")
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
typeText(injector.parser.parseKeys("gn")) typeText(injector.parser.parseKeys("gn"))
assertOffset(7) assertOffset(7)
assertSelection("test") assertSelection("test")

View File

@ -11,10 +11,9 @@ import com.intellij.idea.TestFor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.action.motion.search.SearchWholeWordForwardAction 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.Direction
import com.maddyhome.idea.vim.newapi.vim
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.common.Direction
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,7 +54,7 @@ class VisualSelectPreviousSearchTest : VimTestCase() {
@Test @Test
fun testWithoutSpaces() { fun testWithoutSpaces() {
configureByText("tes<caret>ttest") configureByText("tes<caret>ttest")
VimPlugin.getSearch().setLastSearchState(fixture.editor.vim, "test", "", Direction.FORWARDS) VimPlugin.getSearch().setLastSearchState(fixture.editor, "test", "", Direction.FORWARDS)
typeText(injector.parser.parseKeys("gN")) typeText(injector.parser.parseKeys("gN"))
assertOffset(0) assertOffset(0)
assertSelection("test") assertSelection("test")

View File

@ -10,7 +10,6 @@ package org.jetbrains.plugins.ideavim.action.motion.search
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.common.Direction import com.maddyhome.idea.vim.common.Direction
import com.maddyhome.idea.vim.newapi.vim
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
@ -168,7 +167,7 @@ class SearchAgainPreviousActionTest : VimTestCase() {
private fun doTestWithSearch(keys: String, before: String, after: String) { private fun doTestWithSearch(keys: String, before: String, after: String) {
doTest(keys, before, after) { doTest(keys, before, after) {
VimPlugin.getSearch().setLastSearchState(it.vim, "all", "", Direction.FORWARDS) VimPlugin.getSearch().setLastSearchState(it, "all", "", Direction.FORWARDS)
} }
} }
} }

View File

@ -10,45 +10,26 @@ package org.jetbrains.plugins.ideavim.ex.implementation.commands
import com.intellij.idea.TestFor import com.intellij.idea.TestFor
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.LoggedErrorProcessor
import com.intellij.testFramework.TestLoggerFactory.TestLoggerAssertionError
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.keys
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.history.HistoryConstants import com.maddyhome.idea.vim.history.HistoryConstants
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
import org.jetbrains.plugins.ideavim.ExceptionHandler
import org.jetbrains.plugins.ideavim.OnlyThrowLoggedErrorProcessor
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
import org.jetbrains.plugins.ideavim.assertThrowsLogError
import org.jetbrains.plugins.ideavim.exceptionMappingOwner
import org.jetbrains.plugins.ideavim.waitAndAssert import org.jetbrains.plugins.ideavim.waitAndAssert
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import javax.swing.JTextArea import javax.swing.JTextArea
import kotlin.test.assertEquals
import kotlin.test.assertIs import kotlin.test.assertIs
import kotlin.test.assertTrue
/** /**
* @author vlan * @author vlan
*/ */
class MapCommandTest : VimTestCase() { class MapCommandTest : VimTestCase() {
@AfterEach
fun tearDown() {
injector.keyGroup.removeKeyMapping(exceptionMappingOwner)
}
@TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR) @TestWithoutNeovim(reason = SkipNeovimReason.UNCLEAR)
@Test @Test
fun testMapKtoJ() { fun testMapKtoJ() {
@ -895,14 +876,13 @@ n ,f <Plug>Foo
indicateErrors = true, indicateErrors = true,
null, null,
) )
val exception = assertThrowsLogError<TestLoggerAssertionError> { val exception = assertThrows<Throwable> {
typeText(injector.parser.parseKeys("t")) typeText(injector.parser.parseKeys("t"))
} }
assertIs<ExException>(exception.cause) // Exception is wrapped into LOG.error twice assertIs<ExException>(exception.cause) // The original exception comes from the LOG.error, so we check the cause
assertPluginError(true) assertPluginError(true)
assertPluginErrorMessageContains("E121: Undefined variable: s:mapping") assertPluginErrorMessageContains("E121: Undefined variable: s:mapping")
editor.caretModel.allCarets.forEach { Disposer.dispose(it) }
} }
// todo keyPresses invoked inside a script should have access to the script context // todo keyPresses invoked inside a script should have access to the script context
@ -943,10 +923,11 @@ n ,f <Plug>Foo
""".trimIndent() """.trimIndent()
configureByJavaText(text) configureByJavaText(text)
assertThrowsLogError<ExException> { val exception = assertThrows<Throwable> {
typeText(commandToKeys("inoremap <expr> <cr> unknownFunction() ? '\\<C-y>' : '\\<C-g>u\\<CR>'")) typeText(commandToKeys("inoremap <expr> <cr> unknownFunction() ? '\\<C-y>' : '\\<C-g>u\\<CR>'"))
typeText(injector.parser.parseKeys("i<CR>")) typeText(injector.parser.parseKeys("i<CR>"))
} }
assertIs<ExException>(exception.cause) // The original exception comes from the LOG.error, so we check the cause
assertPluginError(true) assertPluginError(true)
assertPluginErrorMessageContains("E117: Unknown function: unknownFunction") assertPluginErrorMessageContains("E117: Unknown function: unknownFunction")
@ -1116,32 +1097,4 @@ n ,i <Action>(Back)
Cras id tellus in ex imperdiet egestas. Cras id tellus in ex imperdiet egestas.
""".trimIndent()) """.trimIndent())
} }
@TestFor(issues = ["VIM-2929"])
@TestWithoutNeovim(reason = SkipNeovimReason.ACTION_COMMAND)
@Test
fun `mapping to handler with exception`() {
configureByText(
"""
Lorem Ipsum
Lorem ipsum dolor sit amet,
${c}consectetur adipiscing elit
Sed in orci mauris.
Cras id tellus in ex imperdiet egestas.
""".trimIndent()
)
injector.keyGroup.putKeyMapping(MappingMode.NXO, keys("abc"), exceptionMappingOwner, ExceptionHandler(), false)
typeText(commandToKeys("map k abcx"))
val exception = assertThrows<Throwable> {
LoggedErrorProcessor.executeWith<Throwable>(OnlyThrowLoggedErrorProcessor) {
typeText("k")
}
}
assertEquals(ExceptionHandler.exceptionMessage, exception.cause!!.cause!!.message)
assertTrue(KeyHandler.getInstance().keyStack.isEmpty())
}
} }

View File

@ -9,9 +9,7 @@
package org.jetbrains.plugins.ideavim.extension package org.jetbrains.plugins.ideavim.extension
import com.intellij.ide.plugins.PluginManagerCore import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.invokeLater import com.intellij.openapi.application.invokeLater
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.PlatformTestUtil
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
@ -43,23 +41,28 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class OpMappingTest : VimTestCase() { class OpMappingTest : VimTestCase() {
private lateinit var extension: ExtensionBeanClass private var initialized = false
private var disposable: Disposable = Disposer.newDisposable() private lateinit var extension: ExtensionBeanClass
@BeforeEach @BeforeEach
override fun setUp(testInfo: TestInfo) { override fun setUp(testInfo: TestInfo) {
super.setUp(testInfo) super.setUp(testInfo)
if (!initialized) {
initialized = true
extension = TestExtension.createBean() extension = TestExtension.createBean()
VimExtension.EP_NAME.point.registerExtension(extension, disposable) VimExtension.EP_NAME.point.registerExtension(extension, VimPlugin.getInstance())
enableExtensions("TestExtension") enableExtensions("TestExtension")
} }
}
@AfterEach @AfterEach
override fun tearDown(testInfo: TestInfo) { override fun tearDown(testInfo: TestInfo) {
Disposer.dispose(disposable) @Suppress("DEPRECATION")
super.tearDown(testInfo) VimExtension.EP_NAME.point.unregisterExtension(extension)
super.tearDown(super.testInfo)
} }
@Test @Test
@ -135,13 +138,13 @@ class OpMappingTest : VimTestCase() {
typeText(injector.parser.parseKeys("Q")) typeText(injector.parser.parseKeys("Q"))
assertState("I$c found it in a legendary land") assertState("I$c found it in a legendary land")
Disposer.dispose(disposable) @Suppress("DEPRECATION")
disposable = Disposer.newDisposable() VimExtension.EP_NAME.point.unregisterExtension(extension)
assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.instance.owner)) assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.instance.owner))
typeText(injector.parser.parseKeys("Q")) typeText(injector.parser.parseKeys("Q"))
assertState("I$c found it in a legendary land") assertState("I$c found it in a legendary land")
VimExtension.EP_NAME.point.registerExtension(extension, disposable) VimExtension.EP_NAME.point.registerExtension(extension, VimPlugin.getInstance())
assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.instance.owner)) assertEmpty(VimPlugin.getKey().getKeyMappingByOwner(extension.instance.owner))
enableExtensions("TestExtension") enableExtensions("TestExtension")
typeText(injector.parser.parseKeys("Q")) typeText(injector.parser.parseKeys("Q"))
@ -155,12 +158,12 @@ class OpMappingTest : VimTestCase() {
assertState("I$c found it in a legendary land") assertState("I$c found it in a legendary land")
enterCommand("set noTestExtension") enterCommand("set noTestExtension")
Disposer.dispose(disposable) @Suppress("DEPRECATION")
disposable = Disposer.newDisposable() VimExtension.EP_NAME.point.unregisterExtension(extension)
typeText(injector.parser.parseKeys("Q")) typeText(injector.parser.parseKeys("Q"))
assertState("I$c found it in a legendary land") assertState("I$c found it in a legendary land")
VimExtension.EP_NAME.point.registerExtension(extension, disposable) VimExtension.EP_NAME.point.registerExtension(extension, VimPlugin.getInstance())
enableExtensions("TestExtension") enableExtensions("TestExtension")
typeText(injector.parser.parseKeys("Q")) typeText(injector.parser.parseKeys("Q"))
assertState("I ${c}found it in a legendary land") assertState("I ${c}found it in a legendary land")
@ -198,20 +201,19 @@ class PlugExtensionsTest : VimTestCase() {
private lateinit var extension: ExtensionBeanClass private lateinit var extension: ExtensionBeanClass
private var disposable: Disposable = Disposer.newDisposable()
@BeforeEach @BeforeEach
override fun setUp(testInfo: TestInfo) { override fun setUp(testInfo: TestInfo) {
super.setUp(testInfo) super.setUp(testInfo)
configureByText("\n") configureByText("\n")
extension = TestExtension.createBean() extension = TestExtension.createBean()
VimExtension.EP_NAME.point.registerExtension(extension, disposable) VimExtension.EP_NAME.point.registerExtension(extension, VimPlugin.getInstance())
} }
@AfterEach @AfterEach
override fun tearDown(testInfo: TestInfo) { override fun tearDown(testInfo: TestInfo) {
Disposer.dispose(disposable) @Suppress("DEPRECATION")
VimExtension.EP_NAME.point.unregisterExtension(extension)
super.tearDown(super.testInfo) super.tearDown(super.testInfo)
} }
@ -242,20 +244,19 @@ class PlugMissingKeysTest : VimTestCase() {
private lateinit var extension: ExtensionBeanClass private lateinit var extension: ExtensionBeanClass
private var disposable: Disposable = Disposer.newDisposable()
@BeforeEach @BeforeEach
override fun setUp(testInfo: TestInfo) { override fun setUp(testInfo: TestInfo) {
super.setUp(testInfo) super.setUp(testInfo)
configureByText("\n") configureByText("\n")
extension = TestExtension.createBean() extension = TestExtension.createBean()
VimExtension.EP_NAME.point.registerExtension(extension, disposable) VimExtension.EP_NAME.point.registerExtension(extension, VimPlugin.getInstance())
} }
@AfterEach @AfterEach
override fun tearDown(testInfo: TestInfo) { override fun tearDown(testInfo: TestInfo) {
Disposer.dispose(disposable) @Suppress("DEPRECATION")
VimExtension.EP_NAME.point.unregisterExtension(extension)
super.tearDown(super.testInfo) super.tearDown(super.testInfo)
} }

View File

@ -14,16 +14,16 @@ import com.intellij.codeInsight.template.TemplateManager
import com.intellij.codeInsight.template.impl.ConstantNode import com.intellij.codeInsight.template.impl.ConstantNode
import com.intellij.codeInsight.template.impl.TemplateManagerImpl import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
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.group.visual.IdeaSelectionControl import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.VimVisualTimer import com.maddyhome.idea.vim.group.visual.VimVisualTimer
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.listener.VimListenerManager import com.maddyhome.idea.vim.listener.VimListenerManager
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.options.OptionConstants import com.maddyhome.idea.vim.options.OptionConstants
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.state.mode.selectionType
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestOptionConstants import org.jetbrains.plugins.ideavim.TestOptionConstants
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@ -33,7 +33,6 @@ import org.jetbrains.plugins.ideavim.impl.TraceOptions
import org.jetbrains.plugins.ideavim.impl.VimOption import org.jetbrains.plugins.ideavim.impl.VimOption
import org.jetbrains.plugins.ideavim.waitAndAssert import org.jetbrains.plugins.ideavim.waitAndAssert
import org.jetbrains.plugins.ideavim.waitAndAssertMode import org.jetbrains.plugins.ideavim.waitAndAssertMode
import kotlin.test.assertNull
@TraceOptions(TestOptionConstants.selectmode) @TraceOptions(TestOptionConstants.selectmode)
class IdeaVisualControlTest : VimTestCase() { class IdeaVisualControlTest : VimTestCase() {
@ -765,23 +764,6 @@ class IdeaVisualControlTest : VimTestCase() {
assertCaretsVisualAttributes() assertCaretsVisualAttributes()
} }
@OptionTest(VimOption(TestOptionConstants.selectmode, limitedValues = [""]))
@TestWithoutNeovim(reason = SkipNeovimReason.NOT_VIM_TESTING)
fun `test control selection interruption`() {
configureByText(
"""
Lorem Ipsum
I $s${c}found$se it in a legendary land
consectetur adipiscing elit
""".trimIndent(),
)
IdeaSelectionControl.controlNonVimSelectionChange(fixture.editor)
typeText(injector.parser.parseKeys("V"))
assertNull(VimVisualTimer.swingTimer)
}
private fun startDummyTemplate() { private fun startDummyTemplate() {
TemplateManagerImpl.setTemplateTesting(fixture.testRootDisposable) TemplateManagerImpl.setTemplateTesting(fixture.testRootDisposable)
val templateManager = TemplateManager.getInstance(fixture.project) val templateManager = TemplateManager.getInstance(fixture.project)

View File

@ -1,37 +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 org.jetbrains.plugins.ideavim.listener
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.listener.VimListenerTestObject
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class VimListenersTest : VimTestCase() {
@AfterEach
fun tearDown() {
VimListenerTestObject.disposedCounter = 0
VimListenerTestObject.enabled = false
}
@Test
fun `disposable is called on plugin disable`() {
configureByText("XYZ")
VimListenerTestObject.disposedCounter = 0
VimListenerTestObject.enabled = true
VimPlugin.setEnabled(false)
assertEquals(1, VimListenerTestObject.disposedCounter)
VimPlugin.setEnabled(true)
}
}

View File

@ -12,14 +12,11 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.textarea.TextComponentEditorImpl import com.intellij.openapi.editor.textarea.TextComponentEditorImpl
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.junit5.TestDisposable import com.intellij.testFramework.junit5.TestDisposable
import com.intellij.testFramework.replaceService import com.intellij.testFramework.replaceService
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.AfterEach
import org.mockito.Mockito import org.mockito.Mockito
import javax.swing.JTextArea import javax.swing.JTextArea
@ -31,11 +28,6 @@ open class MockTestCase : VimTestCase() {
val editorStub = TextComponentEditorImpl(null, JTextArea()).vim val editorStub = TextComponentEditorImpl(null, JTextArea()).vim
val contextStub: ExecutionContext = DataContext.EMPTY_CONTEXT.vim val contextStub: ExecutionContext = DataContext.EMPTY_CONTEXT.vim
@AfterEach
fun tearDown() {
editorStub.carets().forEach { Disposer.dispose(it.ij) }
}
fun <T : Any> mockService(service: Class<T>): T { fun <T : Any> mockService(service: Class<T>): T {
val mock = Mockito.mock(service) val mock = Mockito.mock(service)
val applicationManager = ApplicationManager.getApplication() val applicationManager = ApplicationManager.getApplication()

View File

@ -22,11 +22,9 @@ import com.maddyhome.idea.vim.api.injector
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.EffectiveOptionValueChangeListener import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
import com.maddyhome.idea.vim.options.NumberOption
import com.maddyhome.idea.vim.options.OptionAccessScope import com.maddyhome.idea.vim.options.OptionAccessScope
import com.maddyhome.idea.vim.options.OptionDeclaredScope import com.maddyhome.idea.vim.options.OptionDeclaredScope
import com.maddyhome.idea.vim.options.StringOption import com.maddyhome.idea.vim.options.StringOption
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
@ -36,10 +34,9 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo import org.junit.jupiter.api.TestInfo
import javax.swing.SwingConstants import javax.swing.SwingConstants
import kotlin.test.assertEquals import kotlin.test.assertContentEquals
private const val defaultValue = "defaultValue" private const val defaultValue = "defaultValue"
private const val defaultNumberValue = 10
@TestWithoutNeovim(reason = SkipNeovimReason.OPTION) @TestWithoutNeovim(reason = SkipNeovimReason.OPTION)
class EffectiveOptionChangeListenerTest : VimTestCase() { class EffectiveOptionChangeListenerTest : VimTestCase() {
@ -112,17 +109,10 @@ class EffectiveOptionChangeListenerTest : VimTestCase() {
return option return option
} }
private fun addNumberOption(scope: OptionDeclaredScope): NumberOption {
val option = NumberOption(optionName, scope, optionName, defaultNumberValue)
injector.optionGroup.addOption(option)
injector.optionGroup.addEffectiveOptionValueChangeListener(option, Listener)
return option
}
private fun assertNotifiedEditors(vararg editors: Editor) { private fun assertNotifiedEditors(vararg editors: Editor) {
val expected = editors.toSet() val sortedExpected = editors.sortedBy { it.virtualFile!!.path }.toTypedArray()
val actual = Listener.notifiedEditors.toSet() val sortedActual = Listener.notifiedEditors.sortedBy { it.virtualFile!!.path }.toTypedArray()
assertEquals(expected, actual) assertContentEquals(sortedExpected, sortedActual)
} }
private fun assertNoNotifications() = assertNotifiedEditors() private fun assertNoNotifications() = assertNotifiedEditors()
@ -282,23 +272,10 @@ class EffectiveOptionChangeListenerTest : VimTestCase() {
@Test @Test
fun `test listener called for all editors when locally modified global-local local-to-buffer option changes at effective scope`() { fun `test listener called for all editors when locally modified global-local local-to-buffer option changes at effective scope`() {
val option = addOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER) val option = addOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER)
injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(originalEditor.vim), VimString("localValue")) injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(otherBufferWindow.vim), VimString("localValue"))
Listener.notifiedEditors.clear() Listener.notifiedEditors.clear()
injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(originalEditor.vim), VimString("newValue")) injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(otherBufferWindow.vim), VimString("newValue"))
assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow)
}
@Test
fun `test listener called for all editors when locally modified number global-local local-to-buffer option changes at effective scope`() {
// When a number (and therefore also toggle) global-local option is set at effective scope, the local value is not
// reset to [Option.unsetValue] but to a copy of the new value. The local editor(s) should still be notified.
val option = addNumberOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER)
injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(originalEditor.vim), VimInt(100))
Listener.notifiedEditors.clear()
injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(originalEditor.vim), VimInt(200))
assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow) assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow)
} }
@ -350,19 +327,6 @@ class EffectiveOptionChangeListenerTest : VimTestCase() {
assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow) assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow)
} }
@Test
fun `test listener called for all editors when locally modified number global-local local-to-window option changes at effective scope`() {
// When a number (and therefore also toggle) global-local option is set at effective scope, the local value is not
// reset to [Option.unsetValue] but to a copy of the new value. The local editor(s) should still be notified.
val option = addNumberOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_WINDOW)
injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(originalEditor.vim), VimInt(100))
Listener.notifiedEditors.clear()
injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(originalEditor.vim), VimInt(200))
assertNotifiedEditors(originalEditor, splitWindow, otherBufferWindow)
}
@Test @Test
fun `test listener not called for locally modified editor when global-local local-to-window option changes at global scope`() { fun `test listener not called for locally modified editor when global-local local-to-window option changes at global scope`() {
val option = addOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_WINDOW) val option = addOption(OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_WINDOW)

View File

@ -129,23 +129,6 @@ class OptionAccessScopeTest: VimTestCase() {
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim))) assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim)))
} }
@Test
fun `test set local-to-buffer option at effective scope to current value changes global value`() {
val defaultValue = VimInt(10)
val option = NumberOption(OPTION_NAME, OptionDeclaredScope.LOCAL_TO_BUFFER, OPTION_NAME, defaultValue)
injector.optionGroup.addOption(option)
val effectiveValue = VimInt(100)
injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(fixture.editor.vim), effectiveValue)
assertEquals(defaultValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(fixture.editor.vim)))
injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim), effectiveValue)
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(fixture.editor.vim)))
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.LOCAL(fixture.editor.vim)))
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim)))
}
// LOCAL_TO_WINDOW // LOCAL_TO_WINDOW
@Test @Test
@ -190,23 +173,6 @@ class OptionAccessScopeTest: VimTestCase() {
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim))) assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim)))
} }
@Test
fun `test set local-to-window option at effective scope to current value changes global value`() {
val defaultValue = VimInt(10)
val option = NumberOption(OPTION_NAME, OptionDeclaredScope.LOCAL_TO_WINDOW, OPTION_NAME, defaultValue)
injector.optionGroup.addOption(option)
val effectiveValue = VimInt(100)
injector.optionGroup.setOptionValue(option, OptionAccessScope.LOCAL(fixture.editor.vim), effectiveValue)
assertEquals(defaultValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(fixture.editor.vim)))
injector.optionGroup.setOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim), effectiveValue)
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(fixture.editor.vim)))
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.LOCAL(fixture.editor.vim)))
assertEquals(effectiveValue, injector.optionGroup.getOptionValue(option, OptionAccessScope.EFFECTIVE(fixture.editor.vim)))
}
// Global-local is tricky. The local value is initially not set, and the global value is used until it is. For string // Global-local is tricky. The local value is initially not set, and the global value is used until it is. For string
// options, this is represented by the local value showing as an empty string. Number options usually use the value -1 // options, this is represented by the local value showing as an empty string. Number options usually use the value -1

View File

@ -8,13 +8,14 @@
package ui package ui
import com.automation.remarks.junit5.Video import com.automation.remarks.junit.VideoRule
import com.automation.remarks.video.annotations.Video
import com.intellij.remoterobot.RemoteRobot import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.fixtures.ContainerFixture import com.intellij.remoterobot.fixtures.ContainerFixture
import com.intellij.remoterobot.steps.CommonSteps
import com.intellij.remoterobot.stepsProcessing.step import com.intellij.remoterobot.stepsProcessing.step
import com.intellij.remoterobot.utils.keyboard import com.intellij.remoterobot.utils.keyboard
import org.assertj.swing.core.MouseButton import org.assertj.swing.core.MouseButton
import org.junit.Rule
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import ui.pages.Editor import ui.pages.Editor
import ui.pages.IdeaFrame import ui.pages.IdeaFrame
@ -24,7 +25,6 @@ import ui.pages.dialog
import ui.pages.editor import ui.pages.editor
import ui.pages.gutter import ui.pages.gutter
import ui.pages.idea import ui.pages.idea
import ui.pages.searchEverywhere
import ui.pages.welcomeFrame import ui.pages.welcomeFrame
import ui.utils.JavaExampleSteps import ui.utils.JavaExampleSteps
import ui.utils.StepsLogger import ui.utils.StepsLogger
@ -38,7 +38,6 @@ import ui.utils.tripleClickOnRight
import ui.utils.uiTest import ui.utils.uiTest
import ui.utils.vimExit import ui.utils.vimExit
import java.awt.Point import java.awt.Point
import java.awt.event.KeyEvent
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -48,19 +47,14 @@ class UiTests {
StepsLogger.init() StepsLogger.init()
} }
private lateinit var commonSteps: CommonSteps @Rule
@JvmField
private val testTextForEditor = """ var videoRule = VideoRule()
|One Two
|Three Four
|Five
""".trimMargin()
@Test @Test
@Video @Video
fun ideaVimTest() = uiTest("ideaVimTest") { fun ideaVimTest() = uiTest("ideaVimTest") {
val sharedSteps = JavaExampleSteps(this) val sharedSteps = JavaExampleSteps(this)
commonSteps = CommonSteps(this)
startNewProject() startNewProject()
Thread.sleep(1000) Thread.sleep(1000)
@ -69,11 +63,16 @@ class UiTests {
Thread.sleep(1000) Thread.sleep(1000)
idea { idea {
waitSmartMode()
createFile("MyDoc.txt", this@uiTest) createFile("MyDoc.txt", this@uiTest)
val editor = editor("MyDoc.txt") { val editor = editor("MyDoc.txt") {
step("Write a text") { step("Write a text") {
injectText(testTextForEditor) injectText(
"""
|One Two
|Three Four
|Five
""".trimMargin(),
)
} }
} }
testSelectTextWithDelay(editor) testSelectTextWithDelay(editor)
@ -90,11 +89,6 @@ class UiTests {
testClickRightFromLineEnd(editor) testClickRightFromLineEnd(editor)
testClickOnWord(editor) testClickOnWord(editor)
testGutterClick(editor) testGutterClick(editor)
testAddNewLineInNormalMode(editor)
testMappingToCtrlOrAltEnter(editor)
`simple enter in insert mode`(editor)
testMilticaretEnter(editor)
`simple enter in select mode`(editor)
reenableIdeaVim(editor) reenableIdeaVim(editor)
createFile("MyTest.java", this@uiTest) createFile("MyTest.java", this@uiTest)
@ -102,7 +96,7 @@ class UiTests {
step("Write a text") { step("Write a text") {
injectText( injectText(
""" """
|class MyTest { |class Main {
| public static void main() { | public static void main() {
| System.out.println("Hello"); | System.out.println("Hello");
| } | }
@ -121,6 +115,7 @@ class UiTests {
private fun closeUnrelated(sharedSteps: JavaExampleSteps) { private fun closeUnrelated(sharedSteps: JavaExampleSteps) {
with(sharedSteps) { with(sharedSteps) {
closeIdeaVimDialog()
closeTipOfTheDay() closeTipOfTheDay()
closeAllTabs() closeAllTabs()
} }
@ -131,7 +126,7 @@ class UiTests {
createNewProjectLink.click() createNewProjectLink.click()
dialog("New Project") { dialog("New Project") {
findText("Java").click() findText("Java").click()
checkBox("Add sample code").unselect() checkBox("Add sample code").select()
button("Create").click() button("Create").click()
} }
} }
@ -200,11 +195,9 @@ class UiTests {
private fun IdeaFrame.testTrackActionId(editor: Editor) { private fun IdeaFrame.testTrackActionId(editor: Editor) {
remoteRobot.invokeActionJs("GotoAction") remoteRobot.invokeActionJs("GotoAction")
editor.keyboard {
val searchEverywhere = this@testTrackActionId.searchEverywhere() enterText("IdeaVim: Track Action Ids")
enter()
commonSteps.invokeAction("VimFindActionIdAction")
keyboard {
escape() escape()
} }
@ -218,7 +211,7 @@ class UiTests {
assertEquals( assertEquals(
""" """
|EditorEscapeclass MyTest { |EditorEscapeclass Main {
| public static void main() { | public static void main() {
| if (true) { | if (true) {
| System.out.println("Hello"); | System.out.println("Hello");
@ -238,7 +231,7 @@ class UiTests {
private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) { private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) {
step("Create $fileName file") { step("Create $fileName file") {
with(projectViewTree) { with(projectViewTree) {
setExpandTimeout(30_000) setExpandTimeout(15_000)
expand(projectName, "src") expand(projectName, "src")
findText("src").click(MouseButton.RIGHT_BUTTON) findText("src").click(MouseButton.RIGHT_BUTTON)
} }
@ -517,187 +510,4 @@ class UiTests {
vimExit() vimExit()
} }
// For VIM-3159
private fun ContainerFixture.testAddNewLineInNormalMode(editor: Editor) {
println("Run testAddNewLineInNormalMode...")
commonSteps.invokeAction("EditorStartNewLineBefore")
assertEquals(
"""
|
|One Two
|Three Four
|Five
""".trimMargin(), editor.text
)
editor.injectText(testTextForEditor)
commonSteps.invokeAction("EditorStartNewLine")
assertEquals(
"""
|One Two
|
|Three Four
|Five
""".trimMargin(), editor.text
)
editor.injectText(testTextForEditor)
vimExit()
}
// For VIM-3190
private fun ContainerFixture.testMappingToCtrlOrAltEnter(editor: Editor) {
println("Run testMappingToCtrlOrAltEnter...")
keyboard {
enterText(":nmap <C-Enter> k")
enter()
enterText(":nmap <A-Enter> G")
enter()
}
// Set up initial position
keyboard {
enterText("jll")
}
assertEquals(10, editor.caretOffset)
// Checking C-ENTER
keyboard {
pressing(KeyEvent.VK_CONTROL) { enter() }
}
assertEquals(2, editor.caretOffset)
// Checking A-ENTER
keyboard {
pressing(KeyEvent.VK_ALT) { enter() }
}
assertEquals(19, editor.caretOffset)
vimExit()
}
// For VIM-3186
private fun ContainerFixture.testMilticaretEnter(editor: Editor) {
println("Run testMilticaretEnter...")
keyboard {
pressing(KeyEvent.VK_ALT) {
pressing(KeyEvent.VK_SHIFT) {
findText("One").click()
findText("Three").click()
findText("Five").click()
}
}
enterText("A")
enter()
}
assertEquals(3, editor.caretCount)
assertEquals(
"""
|One Two
|
|Three Four
|
|Five
|
""".trimMargin(), editor.text
)
// Reset state
keyboard {
escape()
escape()
}
assertEquals(1, editor.caretCount)
editor.injectText(testTextForEditor)
vimExit()
}
private fun ContainerFixture.`simple enter in insert mode`(editor: Editor) {
println("Run test 'simple enter in insert mode'...")
// Start of file
keyboard {
enterText("i")
enter()
}
assertEquals(
"""
|
|One Two
|Three Four
|Five
""".trimMargin(),
editor.text
)
// Middle of file
findText("Four").click()
keyboard { enter() }
assertEquals(
"""
|
|One Two
|Three
|Four
|Five
""".trimMargin(),
editor.text
)
// End of file
val fivePoint = findText("Five").point
val endOfLine = Point(fivePoint.x + 50, fivePoint.y)
click(endOfLine)
keyboard { enter() }
assertEquals(
"""
|
|One Two
|Three
|Four
|Five
|
""".trimMargin(),
editor.text
)
editor.injectText(testTextForEditor)
vimExit()
}
private fun ContainerFixture.`simple enter in select mode`(editor: Editor) {
println("Run test 'simple enter in select mode'...")
findText("Four").doubleClick()
keyboard {
pressing(KeyEvent.VK_CONTROL) { enterText("g") }
enter()
}
assertEquals(
"""
|One Two
|Three
|
|Five
""".trimMargin(),
editor.text
)
editor.injectText(testTextForEditor)
vimExit()
}
} }

View File

@ -44,9 +44,6 @@ class Editor(
val caretOffset: Int val caretOffset: Int
get() = callJs("component.getEditor().getCaretModel().getOffset()", runInEdt = true) get() = callJs("component.getEditor().getCaretModel().getOffset()", runInEdt = true)
val caretCount: Int
get() = callJs("component.getEditor().getCaretModel().getCaretCount()", runInEdt = true)
val isBlockCursor: Boolean val isBlockCursor: Boolean
// get() = callJs("component.getEditor().getSettings().isBlockCursor()", true) // get() = callJs("component.getEditor().getSettings().isBlockCursor()", true)
// Doesn't work at the moment because remote robot can't resolve classes from a plugin classloader // Doesn't work at the moment because remote robot can't resolve classes from a plugin classloader

View File

@ -51,14 +51,6 @@ class IdeaFrame(
} }
} }
fun waitSmartMode(timeout: Duration = Duration.ofMinutes(5)) {
step("Wait for smart mode") {
waitFor(duration = timeout, interval = Duration.ofSeconds(5)) {
isDumbMode().not()
}
}
}
private fun isDumbMode(): Boolean { private fun isDumbMode(): Boolean {
return callJs("com.intellij.openapi. project.DumbService.isDumb(component.project);", true) return callJs("com.intellij.openapi. project.DumbService.isDumb(component.project);", true)
} }

View File

@ -1,31 +0,0 @@
/*
* 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 ui.pages
import com.intellij.remoterobot.RemoteRobot
import com.intellij.remoterobot.data.RemoteComponent
import com.intellij.remoterobot.fixtures.CommonContainerFixture
import com.intellij.remoterobot.fixtures.ContainerFixture
import com.intellij.remoterobot.fixtures.FixtureName
import com.intellij.remoterobot.search.locators.byXpath
@JvmOverloads
fun ContainerFixture.searchEverywhere(function: SearchEverywhere.() -> Unit = {}): SearchEverywhere {
return find<SearchEverywhere>(
byXpath("Search Everywhere", "//div[@accessiblename='Search everywhere' and @class='SearchEverywhereUI']"),
)
.apply { runJs("robot.moveMouse(component);") }
.apply(function)
}
@FixtureName("SearchEverywhere")
class SearchEverywhere(
remoteRobot: RemoteRobot,
remoteComponent: RemoteComponent,
) : CommonContainerFixture(remoteRobot, remoteComponent)

View File

@ -17,11 +17,19 @@ import com.intellij.remoterobot.utils.Keyboard
import ui.pages.DialogFixture import ui.pages.DialogFixture
import ui.pages.DialogFixture.Companion.byTitle import ui.pages.DialogFixture.Companion.byTitle
import ui.pages.IdeaFrame import ui.pages.IdeaFrame
import ui.pages.dialog
import ui.pages.idea
class JavaExampleSteps(private val remoteRobot: RemoteRobot) { class JavaExampleSteps(private val remoteRobot: RemoteRobot) {
@Suppress("unused") @Suppress("unused")
private val keyboard: Keyboard = Keyboard(remoteRobot) private val keyboard: Keyboard = Keyboard(remoteRobot)
fun closeIdeaVimDialog() = optionalStep("Close Idea Vim dialog if it appears") {
remoteRobot.idea {
dialog("IdeaVim") { button("Yes").click() }
}
}
fun closeTipOfTheDay() = optionalStep("Close Tip of the Day if it appears") { fun closeTipOfTheDay() = optionalStep("Close Tip of the Day if it appears") {
val idea: IdeaFrame = remoteRobot.find(IdeaFrame::class.java) val idea: IdeaFrame = remoteRobot.find(IdeaFrame::class.java)
idea.dumbAware { idea.dumbAware {

View File

@ -28,7 +28,7 @@ import java.util.*
public class AutoIndentLinesVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class AutoIndentLinesVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE, CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

View File

@ -13,10 +13,13 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.CharacterHelper import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
/** /**
* @author vlan * @author vlan
@ -25,6 +28,8 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeCaseLowerVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -14,10 +14,13 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.CharacterHelper import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
/** /**
* @author vlan * @author vlan
@ -26,6 +29,8 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
public class ChangeCaseToggleVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeCaseToggleVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -13,10 +13,13 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.CharacterHelper import com.maddyhome.idea.vim.helper.CharacterHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
/** /**
* @author vlan * @author vlan
@ -25,6 +28,8 @@ import com.maddyhome.idea.vim.helper.CharacterHelper
public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeCaseUpperVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -16,6 +16,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
@ -26,7 +27,7 @@ import java.util.*
public class ChangeCharactersAction : ChangeEditorActionHandler.ForEachCaret() { public class ChangeCharactersAction : ChangeEditorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT) override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT, FLAG_MULTIKEY_UNDO)
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,

View File

@ -16,6 +16,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
@ -26,7 +27,7 @@ import java.util.*
public class ChangeEndOfLineAction : ChangeEditorActionHandler.ForEachCaret() { public class ChangeEndOfLineAction : ChangeEditorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT) override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT, FLAG_MULTIKEY_UNDO)
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,

View File

@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT import com.maddyhome.idea.vim.command.CommandFlags.FLAG_NO_REPEAT_INSERT
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
@ -27,7 +28,7 @@ import java.util.*
public class ChangeLineAction : ChangeEditorActionHandler.ForEachCaret() { public class ChangeLineAction : ChangeEditorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT) override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_NO_REPEAT_INSERT, FLAG_MULTIKEY_UNDO)
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,

View File

@ -14,13 +14,18 @@ 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
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
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.ChangeEditorActionHandler import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
@CommandOrMotion(keys = ["R"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["R"], modes = [Mode.NORMAL])
public class ChangeReplaceAction : ChangeEditorActionHandler.SingleExecution() { public class ChangeReplaceAction : ChangeEditorActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MULTIKEY_UNDO)
override fun execute( override fun execute(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,

View File

@ -14,9 +14,12 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
/** /**
* @author vlan * @author vlan
@ -25,6 +28,8 @@ import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
public class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MULTIKEY_UNDO, CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -34,7 +34,7 @@ public class ChangeVisualCharacterAction : VisualOperatorActionHandler.ForEachCa
override val argumentType: Argument.Type = Argument.Type.DIGRAPH override val argumentType: Argument.Type = Argument.Type.DIGRAPH
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_ALLOW_DIGRAPH) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_ALLOW_DIGRAPH, CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

View File

@ -17,7 +17,9 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector 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.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_EXIT_VISUAL
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
@ -33,7 +35,7 @@ import java.util.*
public class ChangeVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_MOT_LINEWISE) override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_MOT_LINEWISE, FLAG_MULTIKEY_UNDO, FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

View File

@ -17,7 +17,9 @@ import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector 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.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_EXIT_VISUAL
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MOT_LINEWISE
import com.maddyhome.idea.vim.command.CommandFlags.FLAG_MULTIKEY_UNDO
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
@ -33,7 +35,7 @@ import java.util.*
public class ChangeVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() { public class ChangeVisualLinesEndAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_MOT_LINEWISE) override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_MOT_LINEWISE, FLAG_MULTIKEY_UNDO, FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

View File

@ -28,7 +28,7 @@ import java.util.*
public class ReformatCodeVisualAction : VisualOperatorActionHandler.ForEachCaret() { public class ReformatCodeVisualAction : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_MOT_LINEWISE, CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

View File

@ -14,12 +14,16 @@ import com.maddyhome.idea.vim.api.VimCaret
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.Command import com.maddyhome.idea.vim.command.Command
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.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*
public sealed class IncNumber(public val inc: Int, private val avalanche: Boolean) : VisualOperatorActionHandler.ForEachCaret() { public sealed class IncNumber(public val inc: Int, private val avalanche: Boolean) : VisualOperatorActionHandler.ForEachCaret() {
override val type: Command.Type = Command.Type.CHANGE override val type: Command.Type = Command.Type.CHANGE
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
override fun executeAction( override fun executeAction(
editor: VimEditor, editor: VimEditor,

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