mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2026-04-10 15:57:42 +02:00
Compare commits
31 Commits
customized
...
customized
| Author | SHA1 | Date | |
|---|---|---|---|
|
7c4efe496d
|
|||
|
19aa1f928c
|
|||
|
b7d17bbb6e
|
|||
|
91546dd0d7
|
|||
|
89e1511860
|
|||
|
507bbff1c3
|
|||
|
716956a30f
|
|||
|
dd33e39850
|
|||
|
ebc77454ab
|
|||
|
c9193cb6d4
|
|||
|
13246c0a80
|
|||
|
b0ff57a4f5
|
|||
|
f4e0684ca8
|
|||
|
3a3e7952b1
|
|||
|
1ff6066e33
|
|||
|
3a9abba410
|
|||
|
510f8f948e
|
|||
|
b623bf739c
|
|||
|
c99d97b3bc
|
|||
|
6b8eb8952f
|
|||
|
25d70ee975
|
|||
|
cbc9637d17
|
|||
|
0d893d9961
|
|||
|
4ac3a1eaaa
|
|||
|
86a6e9643f
|
|||
|
8b06078607
|
|||
|
924455907a
|
|||
|
40367859b8
|
|||
|
45f7934d71
|
|||
|
0880e5f935
|
|||
|
8af3788379
|
16
.idea/runConfigurations/Split_Frontend_Debugger.xml
generated
16
.idea/runConfigurations/Split_Frontend_Debugger.xml
generated
@@ -1,16 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Split Frontend Debugger" type="Remote" folderName="Split Mode">
|
|
||||||
<module name="ideavim" />
|
|
||||||
<option name="USE_SOCKET_TRANSPORT" value="true" />
|
|
||||||
<option name="SERVER_MODE" value="false" />
|
|
||||||
<option name="SHMEM_ADDRESS" />
|
|
||||||
<option name="HOST" value="localhost" />
|
|
||||||
<option name="PORT" value="5006" />
|
|
||||||
<option name="AUTO_RESTART" value="false" />
|
|
||||||
<RunnerSettings RunnerId="Debug">
|
|
||||||
<option name="DEBUG_PORT" value="5006" />
|
|
||||||
<option name="LOCAL" value="false" />
|
|
||||||
</RunnerSettings>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
24
.idea/runConfigurations/Start_CLion_with_IdeaVim.xml
generated
24
.idea/runConfigurations/Start_CLion_with_IdeaVim.xml
generated
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start CLion with IdeaVim" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runClion" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start CLion with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms (Split)">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runCLionSplitMode" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle" folderName="Split Mode">
|
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start IJ with IdeaVim (Split Mode Debug Frontend)" type="GradleRunConfiguration" factoryName="Gradle" folderName="Split Mode">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runIdeSplitModeDebugFrontend" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start PyCharm with IdeaVim" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runPycharm" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start PyCharm with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms (Split)">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runPycharmSplitMode" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
24
.idea/runConfigurations/Start_Rider_with_IdeaVim.xml
generated
24
.idea/runConfigurations/Start_Rider_with_IdeaVim.xml
generated
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start Rider with IdeaVim" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runRider" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start WebStorm with IdeaVim" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runWebstorm" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start WebStorm with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle" folderName="Platforms (Split)">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runWebstormSplitMode" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
|
|
||||||
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
|
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
|
||||||
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
|
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
@@ -27,11 +26,11 @@ buildscript {
|
|||||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.6.0.202603022253-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.6.0.202603022253-r")
|
||||||
classpath("org.kohsuke:github-api:1.305")
|
classpath("org.kohsuke:github-api:1.305")
|
||||||
|
|
||||||
classpath("io.ktor:ktor-client-core:3.4.2")
|
classpath("io.ktor:ktor-client-core:3.4.1")
|
||||||
classpath("io.ktor:ktor-client-cio:3.4.2")
|
classpath("io.ktor:ktor-client-cio:3.4.1")
|
||||||
classpath("io.ktor:ktor-client-auth:3.4.2")
|
classpath("io.ktor:ktor-client-auth:3.4.1")
|
||||||
classpath("io.ktor:ktor-client-content-negotiation:3.4.2")
|
classpath("io.ktor:ktor-client-content-negotiation:3.4.1")
|
||||||
classpath("io.ktor:ktor-serialization-kotlinx-json:3.4.2")
|
classpath("io.ktor:ktor-serialization-kotlinx-json:3.4.1")
|
||||||
|
|
||||||
// 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")
|
||||||
@@ -113,7 +112,7 @@ dependencies {
|
|||||||
testFramework(TestFrameworkType.Platform)
|
testFramework(TestFrameworkType.Platform)
|
||||||
testFramework(TestFrameworkType.JUnit5)
|
testFramework(TestFrameworkType.JUnit5)
|
||||||
|
|
||||||
compatiblePlugin("com.intellij.classic.ui")
|
plugin("com.intellij.classic.ui", "261.22158.185")
|
||||||
|
|
||||||
pluginModule(runtimeOnly(project(":modules:ideavim-common")))
|
pluginModule(runtimeOnly(project(":modules:ideavim-common")))
|
||||||
pluginModule(runtimeOnly(project(":modules:ideavim-frontend")))
|
pluginModule(runtimeOnly(project(":modules:ideavim-frontend")))
|
||||||
@@ -226,30 +225,6 @@ tasks {
|
|||||||
// localPath = file("/Users/{user}/Applications/WebStorm.app")
|
// localPath = file("/Users/{user}/Applications/WebStorm.app")
|
||||||
// }
|
// }
|
||||||
|
|
||||||
val runPycharm by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.PyCharmProfessional
|
|
||||||
version = "2025.3.2"
|
|
||||||
task {
|
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val runWebstorm by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.WebStorm
|
|
||||||
version = "2025.3.2"
|
|
||||||
task {
|
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val runClion by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.CLion
|
|
||||||
version = "2025.3.2"
|
|
||||||
task {
|
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
|
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
|
||||||
task {
|
task {
|
||||||
jvmArgumentProviders += CommandLineArgumentProvider {
|
jvmArgumentProviders += CommandLineArgumentProvider {
|
||||||
@@ -272,55 +247,6 @@ tasks {
|
|||||||
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
|
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
|
||||||
splitMode = true
|
splitMode = true
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val runWebstormSplitMode by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.WebStorm
|
|
||||||
version = "2025.3.2"
|
|
||||||
splitMode = true
|
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val runRider by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.Rider
|
|
||||||
version = "2026.1"
|
|
||||||
task {
|
|
||||||
systemProperty("idea.log.debug.categories", "com.maddyhome.idea.vim.handler.EditorHandlersChainLogger")
|
|
||||||
}
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val runCLionSplitMode by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.CLion
|
|
||||||
version = "2025.3.2"
|
|
||||||
splitMode = true
|
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val runPycharmSplitMode by intellijPlatformTesting.runIde.registering {
|
|
||||||
type = IntelliJPlatformType.PyCharmProfessional
|
|
||||||
version = "2025.3.2"
|
|
||||||
splitMode = true
|
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run split mode with a JDWP debug agent on the frontend (JetBrains Client) process.
|
// Run split mode with a JDWP debug agent on the frontend (JetBrains Client) process.
|
||||||
@@ -329,11 +255,6 @@ tasks {
|
|||||||
splitMode = true
|
splitMode = true
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareSandboxTask {
|
prepareSandboxTask {
|
||||||
val sandboxDir = project.layout.buildDirectory.dir("idea-sandbox").map { it.asFile }
|
val sandboxDir = project.layout.buildDirectory.dir("idea-sandbox").map { it.asFile }
|
||||||
doLast {
|
doLast {
|
||||||
@@ -365,12 +286,6 @@ tasks {
|
|||||||
val testIdeSplitMode by intellijPlatformTesting.testIde.registering {
|
val testIdeSplitMode by intellijPlatformTesting.testIde.registering {
|
||||||
splitMode = true
|
splitMode = true
|
||||||
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
splitModeTarget = SplitModeAware.SplitModeTarget.BOTH
|
||||||
|
|
||||||
plugins {
|
|
||||||
plugin("AceJump", "3.8.22")
|
|
||||||
plugin("org.jetbrains.IdeaVim-EasyMotion", "1.16")
|
|
||||||
}
|
|
||||||
|
|
||||||
task {
|
task {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ ideaVersion=2026.1
|
|||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||||
ideaType=IU
|
ideaType=IU
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=chylex-56
|
version=chylex-54
|
||||||
javaVersion=21
|
javaVersion=21
|
||||||
remoteRobotVersion=0.11.23
|
remoteRobotVersion=0.11.23
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
|||||||
2
gradlew
vendored
2
gradlew
vendored
@@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
@@ -165,27 +165,25 @@ internal class FileRemoteApiImpl : FileRemoteApi {
|
|||||||
// ======================== Private helpers ========================
|
// ======================== Private helpers ========================
|
||||||
|
|
||||||
private fun findFile(filename: String, project: Project): VirtualFile? {
|
private fun findFile(filename: String, project: Project): VirtualFile? {
|
||||||
|
var found: VirtualFile?
|
||||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||||
val relativePath = filename.substring(2)
|
val relativePath = filename.substring(2)
|
||||||
val dir = System.getProperty("user.home")
|
val dir = System.getProperty("user.home")
|
||||||
logger.debug { "home dir file" }
|
logger.debug { "home dir file" }
|
||||||
logger.debug { "looking for $relativePath in $dir" }
|
logger.debug { "looking for $relativePath in $dir" }
|
||||||
return LocalFileSystem.getInstance().refreshAndFindFileByNioFile(Path(dir, relativePath))
|
found = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(Path(dir, relativePath))
|
||||||
|
} else {
|
||||||
|
found = VirtualFileManager.getInstance().findFileByNioPath(Path(filename))
|
||||||
|
|
||||||
|
if (found == null) {
|
||||||
|
found = findByNameInContentRoots(filename, project)
|
||||||
|
if (found == null) {
|
||||||
|
found = findByNameInProject(filename, project)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val basePath = project.basePath
|
return found
|
||||||
if (basePath != null) {
|
|
||||||
val baseDir = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(Path(basePath))
|
|
||||||
baseDir?.findFileByRelativePath(filename)?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualFileManager.getInstance().findFileByNioPath(Path(filename))?.let { return it }
|
|
||||||
|
|
||||||
findByNameInContentRoots(filename, project)?.let { return it }
|
|
||||||
|
|
||||||
findByNameInProject(filename, project)?.let { return it }
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildFileInfoMessage(editor: Editor, project: Project, fullPath: Boolean): String {
|
private fun buildFileInfoMessage(editor: Editor, project: Project, fullPath: Boolean): String {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugin id="com.intellij.modules.rider"/>
|
<module name="com.intellij.modules.rider"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<projectListeners>
|
<projectListeners>
|
||||||
<listener class="com.maddyhome.idea.vim.listener.RiderActionListener"
|
<listener class="com.maddyhome.idea.vim.listener.RiderActionListener"
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ dependencies {
|
|||||||
testImplementation("org.junit.jupiter:junit-jupiter:6.0.3")
|
testImplementation("org.junit.jupiter:junit-jupiter:6.0.3")
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core:3.4.2")
|
implementation("io.ktor:ktor-client-core:3.4.1")
|
||||||
implementation("io.ktor:ktor-client-cio:3.4.2")
|
implementation("io.ktor:ktor-client-cio:3.4.1")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:3.4.2")
|
implementation("io.ktor:ktor-client-content-negotiation:3.4.1")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.2")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.1")
|
||||||
implementation("io.ktor:ktor-client-auth:3.4.2")
|
implementation("io.ktor:ktor-client-auth:3.4.1")
|
||||||
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
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.intellij.openapi.util.Disposer;
|
|||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.config.VimState;
|
import com.maddyhome.idea.vim.config.VimState;
|
||||||
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
|
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
|
||||||
import com.maddyhome.idea.vim.group.KeyGroup;
|
|
||||||
import com.maddyhome.idea.vim.group.VimNotifications;
|
import com.maddyhome.idea.vim.group.VimNotifications;
|
||||||
import com.maddyhome.idea.vim.group.VimWindowGroup;
|
import com.maddyhome.idea.vim.group.VimWindowGroup;
|
||||||
import com.maddyhome.idea.vim.history.VimHistory;
|
import com.maddyhome.idea.vim.history.VimHistory;
|
||||||
@@ -131,12 +130,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
return VimInjectorKt.getInjector().getHistoryGroup();
|
return VimInjectorKt.getInjector().getHistoryGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull KeyGroup getKey() {
|
public static @NotNull VimKeyGroup getKey() {
|
||||||
return ((KeyGroup)VimInjectorKt.getInjector().getKeyGroup());
|
return VimInjectorKt.getInjector().getKeyGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable KeyGroup getKeyIfCreated() {
|
public static @Nullable VimKeyGroup getKeyIfCreated() {
|
||||||
return ApplicationManager.getApplication().getServiceIfCreated(KeyGroup.class);
|
return ApplicationManager.getApplication().getServiceIfCreated(VimKeyGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull VimWindowGroup getWindow() {
|
public static @NotNull VimWindowGroup getWindow() {
|
||||||
@@ -338,7 +337,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (element.getChild("shortcut-conflicts") != null) {
|
if (element.getChild("shortcut-conflicts") != null) {
|
||||||
getKey().loadShortcutConflictsData(element);
|
((VimKeyGroupBase)getKey()).loadShortcutConflictsData(element);
|
||||||
}
|
}
|
||||||
if (element.getChild("editor") != null) {
|
if (element.getChild("editor") != null) {
|
||||||
getEditor().loadEditorStateData(element);
|
getEditor().loadEditorStateData(element);
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.extensions.ExtensionPointListener
|
import com.intellij.openapi.extensions.ExtensionPointListener
|
||||||
import com.intellij.openapi.extensions.PluginDescriptor
|
import com.intellij.openapi.extensions.PluginDescriptor
|
||||||
import com.intellij.vim.api.VimInitApi
|
|
||||||
import com.maddyhome.idea.vim.api.VimExtensionRegistrator
|
import com.maddyhome.idea.vim.api.VimExtensionRegistrator
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.setToggleOption
|
import com.maddyhome.idea.vim.api.setToggleOption
|
||||||
@@ -21,6 +20,7 @@ import com.maddyhome.idea.vim.key.MappingOwner.Plugin.Companion.remove
|
|||||||
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.ToggleOption
|
import com.maddyhome.idea.vim.options.ToggleOption
|
||||||
|
import com.intellij.vim.api.VimInitApi
|
||||||
import com.maddyhome.idea.vim.statistic.ExtensionTracking
|
import com.maddyhome.idea.vim.statistic.ExtensionTracking
|
||||||
import com.maddyhome.idea.vim.thinapi.VimApiImpl
|
import com.maddyhome.idea.vim.thinapi.VimApiImpl
|
||||||
|
|
||||||
@@ -106,13 +106,9 @@ class VimExtensionRegistrar : VimExtensionRegistrator {
|
|||||||
override fun enableDelayedExtensions() {
|
override fun enableDelayedExtensions() {
|
||||||
delayedExtensionEnabling.forEach {
|
delayedExtensionEnabling.forEach {
|
||||||
val name = it.name ?: it.instance.name
|
val name = it.name ?: it.instance.name
|
||||||
try {
|
val initApi = createVimApi(name)
|
||||||
val initApi = createVimApi(name)
|
it.instance.init(initApi)
|
||||||
it.instance.init(initApi)
|
logger.info("IdeaVim extension '$name' initialized")
|
||||||
logger.info("IdeaVim extension '$name' initialized")
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
logger.error("Failed to initialize IdeaVim extension '$name'", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delayedExtensionEnabling.clear()
|
delayedExtensionEnabling.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,17 +241,13 @@ 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 (and CLion Nova) uses a separate handler for esc to close the completion. IdeaOnlyEscapeHandlerAction is especially
|
||||||
* designed 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
|
||||||
* behaviour causes a lot of complaining from users. Since the rider handler gets execution control, we don't
|
* behaviour causes a lot of complaining from users. Since the rider handler gets execution control, we don't
|
||||||
* receive an event and don't exit the insert mode.
|
* receive an event and don't exit the insert mode.
|
||||||
* To fix it, this special handler exists only for rider and stands before the rider's handler. We don't execute the
|
* To fix it, this special handler exists only for rider and stands before the rider's handler. We don't execute the
|
||||||
* handler from rider because the autocompletion is closed automatically anyway.
|
* handler from rider because the autocompletion is closed automatically anyway.
|
||||||
*
|
|
||||||
* NOTE: This handler only works when octopus is enabled (non-Rider IDEs). For Rider, where octopus is disabled
|
|
||||||
* (VIM-3815) and Escape is consumed by the popup manager before the EditorEscape chain fires, the fix is in
|
|
||||||
* [com.maddyhome.idea.vim.listener.IdeaSpecifics.LookupTopicListener] via a LookupListener.
|
|
||||||
*/
|
*/
|
||||||
internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
|
internal class VimEscForRiderHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
|
||||||
override val key: String = "<Esc>"
|
override val key: String = "<Esc>"
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
|||||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
||||||
import com.maddyhome.idea.vim.ide.isClionNova
|
|
||||||
import com.maddyhome.idea.vim.ide.isRider
|
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.initInjector
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
@@ -352,16 +350,6 @@ internal object IdeaSpecifics {
|
|||||||
if (newLookup.editor.isIdeaVimDisabledHere) return
|
if (newLookup.editor.isIdeaVimDisabledHere) return
|
||||||
|
|
||||||
(VimPlugin.getKey() as VimKeyGroupBase).registerShortcutsForLookup(newLookup)
|
(VimPlugin.getKey() as VimKeyGroupBase).registerShortcutsForLookup(newLookup)
|
||||||
|
|
||||||
// In Rider/CLion Nova, octopus is disabled (VIM-3815) and Escape is consumed by the popup manager
|
|
||||||
// (due to LookupSummaryInfo popup) before the action system runs, so IdeaVim never sees it.
|
|
||||||
// Listen for explicit lookup cancellation (Escape) to exit insert mode.
|
|
||||||
// Note: we check isRider/isClionNova specifically, not !isOctopusEnabled(), because
|
|
||||||
// JetBrains Client (split mode) also has octopus disabled but doesn't need this workaround,
|
|
||||||
// and isCanceledExplicitly can be true for non-Escape keys (e.g. space) in that environment.
|
|
||||||
if (isRider() || isClionNova()) {
|
|
||||||
newLookup.addLookupListener(RiderEscLookupListener(newLookup.editor))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup closed
|
// Lookup closed
|
||||||
@@ -373,20 +361,6 @@ internal object IdeaSpecifics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* In Rider/CLion Nova, octopus is disabled (VIM-3815) and Escape is consumed by the popup manager
|
|
||||||
* (due to LookupSummaryInfo parameter info popup) before the action system runs, so IdeaVim never sees it.
|
|
||||||
* This listener exits insert mode when the lookup is explicitly cancelled (Escape).
|
|
||||||
*/
|
|
||||||
private class RiderEscLookupListener(private val editor: Editor) : com.intellij.codeInsight.lookup.LookupListener {
|
|
||||||
override fun lookupCanceled(event: com.intellij.codeInsight.lookup.LookupEvent) {
|
|
||||||
if (event.isCanceledExplicitly && editor.vim.mode is Mode.INSERT) {
|
|
||||||
editor.vim.exitInsertMode(injector.executionContextManager.getEditorExecutionContext(editor.vim))
|
|
||||||
KeyHandler.getInstance().reset(editor.vim)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region Hide Vim search highlights when showing IntelliJ search results
|
//region Hide Vim search highlights when showing IntelliJ search results
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import com.intellij.openapi.editor.Caret
|
|||||||
import com.intellij.openapi.editor.LogicalPosition
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
import com.intellij.openapi.editor.VisualPosition
|
import com.intellij.openapi.editor.VisualPosition
|
||||||
import com.maddyhome.idea.vim.api.BufferPosition
|
import com.maddyhome.idea.vim.api.BufferPosition
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
|
||||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
|
import com.maddyhome.idea.vim.api.LocalMarkStorage
|
||||||
import com.maddyhome.idea.vim.api.SelectionInfo
|
import com.maddyhome.idea.vim.api.SelectionInfo
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
@@ -199,12 +198,3 @@ class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
|||||||
|
|
||||||
override fun hashCode(): Int = this.caret.hashCode()
|
override fun hashCode(): Int = this.caret.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
val Caret.vim: VimCaret
|
|
||||||
get() = VimEditorFactory.getInstance().createVimCaret(this)
|
|
||||||
|
|
||||||
val VimCaret.ij: Caret
|
|
||||||
get() = VimEditorFactory.getInstance().extractCaret(this)
|
|
||||||
|
|
||||||
val ImmutableVimCaret.ij: Caret
|
|
||||||
get() = VimEditorFactory.getInstance().extractCaret(this)
|
|
||||||
|
|||||||
@@ -671,12 +671,3 @@ class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val Editor.vim: VimEditor
|
|
||||||
get() = VimEditorFactory.getInstance().createVimEditor(this)
|
|
||||||
|
|
||||||
val VimEditor.ij: Editor
|
|
||||||
get() = VimEditorFactory.getInstance().extractEditor(this)
|
|
||||||
|
|
||||||
val com.intellij.openapi.util.TextRange.vim: TextRange
|
|
||||||
get() = TextRange(this.startOffset, this.endOffset)
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -9,9 +9,7 @@
|
|||||||
package com.maddyhome.idea.vim.newapi
|
package com.maddyhome.idea.vim.newapi
|
||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.maddyhome.idea.vim.api.MessageType
|
||||||
import com.intellij.openapi.project.ProjectManager
|
|
||||||
import com.intellij.openapi.wm.WindowManager
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimMessagesBase
|
import com.maddyhome.idea.vim.api.VimMessagesBase
|
||||||
import com.maddyhome.idea.vim.api.globalOptions
|
import com.maddyhome.idea.vim.api.globalOptions
|
||||||
@@ -25,56 +23,43 @@ internal class IjVimMessages : VimMessagesBase() {
|
|||||||
private var message: String? = null
|
private var message: String? = null
|
||||||
private var error = false
|
private var error = false
|
||||||
private var lastBeepTimeMillis = 0L
|
private var lastBeepTimeMillis = 0L
|
||||||
private var allowClearStatusBarMessage = true
|
|
||||||
|
|
||||||
override fun showStatusBarMessage(editor: VimEditor?, message: String?) {
|
override fun showMessage(editor: VimEditor, message: String?) {
|
||||||
fun setStatusBarMessage(project: Project, message: String?) {
|
showMessageInternal(editor, message, MessageType.STANDARD)
|
||||||
WindowManager.getInstance().getStatusBar(project)?.let {
|
}
|
||||||
it.info = if (message.isNullOrBlank()) "" else "Vim - $message"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
override fun showErrorMessage(editor: VimEditor, message: String?) {
|
||||||
|
showMessageInternal(editor, message, MessageType.ERROR)
|
||||||
|
indicateError()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showMessageInternal(editor: VimEditor, message: String?, messageType: MessageType) {
|
||||||
this.message = message
|
this.message = message
|
||||||
|
|
||||||
val project = editor?.ij?.project
|
if (message.isNullOrBlank()) {
|
||||||
if (project != null) {
|
clearStatusBarMessage()
|
||||||
setStatusBarMessage(project, message)
|
return
|
||||||
} else {
|
|
||||||
// TODO: We really shouldn't set the status bar text for other projects. That's rude.
|
|
||||||
ProjectManager.getInstance().openProjects.forEach {
|
|
||||||
setStatusBarMessage(it, message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redraw happens automatically based on changes or scrolling. If we've just set the message (e.g., searching for a
|
val context = injector.executionContextManager.getEditorExecutionContext(editor)
|
||||||
// string, hitting the bottom and scrolling to the top), make sure we don't immediately clear it when scrolling.
|
injector.outputPanel.output(editor, context, message, messageType)
|
||||||
allowClearStatusBarMessage = false
|
}
|
||||||
ApplicationManager.getApplication().invokeLater {
|
|
||||||
allowClearStatusBarMessage = true
|
@Suppress("DEPRECATION")
|
||||||
|
override fun showStatusBarMessage(editor: VimEditor?, message: String?) {
|
||||||
|
if (editor != null) {
|
||||||
|
showMessage(editor, message)
|
||||||
|
} else {
|
||||||
|
// Legacy path for when editor is null - just store the message
|
||||||
|
this.message = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStatusBarMessage(): String? = message
|
override fun getStatusBarMessage(): String? = message
|
||||||
|
|
||||||
// Vim doesn't appear to have a policy about clearing the status bar, other than on "redraw". This can be forced with
|
|
||||||
// <C-L> or the `:redraw` command, but also happens as the screen changes, e.g., when inserting or deleting lines,
|
|
||||||
// scrolling, entering Command-line mode and probably lots more. We should manually clear the status bar when these
|
|
||||||
// things happen.
|
|
||||||
override fun clearStatusBarMessage() {
|
override fun clearStatusBarMessage() {
|
||||||
val currentMessage = message
|
if (message.isNullOrEmpty()) return
|
||||||
if (currentMessage.isNullOrEmpty()) return
|
injector.outputPanel.getCurrentOutputPanel()?.close()
|
||||||
|
|
||||||
// Don't clear the status bar message if we've only just set it
|
|
||||||
if (!allowClearStatusBarMessage) return
|
|
||||||
|
|
||||||
ProjectManager.getInstance().openProjects.forEach { project ->
|
|
||||||
WindowManager.getInstance().getStatusBar(project)?.let { statusBar ->
|
|
||||||
// Only clear the status bar if it's showing our last message
|
|
||||||
if (statusBar.info?.contains(currentMessage) == true) {
|
|
||||||
statusBar.info = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
message = null
|
message = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
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.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
|
||||||
interface VimEditorFactory {
|
interface VimEditorFactory {
|
||||||
fun createVimEditor(editor: Editor): VimEditor
|
fun createVimEditor(editor: Editor): VimEditor
|
||||||
@@ -26,3 +27,21 @@ interface VimEditorFactory {
|
|||||||
fun getInstance(): VimEditorFactory = service()
|
fun getInstance(): VimEditorFactory = service()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Editor.vim: VimEditor
|
||||||
|
get() = VimEditorFactory.getInstance().createVimEditor(this)
|
||||||
|
|
||||||
|
val VimEditor.ij: Editor
|
||||||
|
get() = VimEditorFactory.getInstance().extractEditor(this)
|
||||||
|
|
||||||
|
val Caret.vim: VimCaret
|
||||||
|
get() = VimEditorFactory.getInstance().createVimCaret(this)
|
||||||
|
|
||||||
|
val VimCaret.ij: Caret
|
||||||
|
get() = VimEditorFactory.getInstance().extractCaret(this)
|
||||||
|
|
||||||
|
val ImmutableVimCaret.ij: Caret
|
||||||
|
get() = VimEditorFactory.getInstance().extractCaret(this)
|
||||||
|
|
||||||
|
val com.intellij.openapi.util.TextRange.vim: TextRange
|
||||||
|
get() = TextRange(this.startOffset, this.endOffset)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.wm.impl.IdeBackgroundUtil
|
import com.intellij.openapi.wm.impl.IdeBackgroundUtil
|
||||||
import com.intellij.openapi.wm.impl.ToolWindowManagerImpl
|
import com.intellij.openapi.wm.impl.ToolWindowManagerImpl
|
||||||
import com.intellij.ui.ClientProperty
|
import com.intellij.ui.ClientProperty
|
||||||
|
import com.intellij.ui.JBColor
|
||||||
import com.intellij.ui.components.JBPanel
|
import com.intellij.ui.components.JBPanel
|
||||||
import com.intellij.ui.components.JBScrollPane
|
import com.intellij.ui.components.JBScrollPane
|
||||||
import com.intellij.util.IJSwingUtilities
|
import com.intellij.util.IJSwingUtilities
|
||||||
@@ -24,7 +25,6 @@ import com.maddyhome.idea.vim.api.MessageType
|
|||||||
import com.maddyhome.idea.vim.api.VimOutputPanel
|
import com.maddyhome.idea.vim.api.VimOutputPanel
|
||||||
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.diagnostic.VimLogger
|
|
||||||
import com.maddyhome.idea.vim.helper.requestFocus
|
import com.maddyhome.idea.vim.helper.requestFocus
|
||||||
import com.maddyhome.idea.vim.helper.selectEditorFont
|
import com.maddyhome.idea.vim.helper.selectEditorFont
|
||||||
import com.maddyhome.idea.vim.helper.vimMorePanel
|
import com.maddyhome.idea.vim.helper.vimMorePanel
|
||||||
@@ -36,121 +36,166 @@ import java.awt.event.ComponentAdapter
|
|||||||
import java.awt.event.ComponentEvent
|
import java.awt.event.ComponentEvent
|
||||||
import java.awt.event.KeyAdapter
|
import java.awt.event.KeyAdapter
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import javax.swing.JComponent
|
import javax.swing.JComponent
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
import javax.swing.JRootPane
|
|
||||||
import javax.swing.JScrollPane
|
import javax.swing.JScrollPane
|
||||||
import javax.swing.JTextArea
|
import javax.swing.JTextPane
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
import javax.swing.SwingUtilities
|
import javax.swing.SwingUtilities
|
||||||
|
import javax.swing.text.DefaultCaret
|
||||||
|
import javax.swing.text.SimpleAttributeSet
|
||||||
|
import javax.swing.text.StyleConstants
|
||||||
|
import javax.swing.text.StyledDocument
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This panel displays text in a `more` like window and implements [VimOutputPanel].
|
* Panel that displays text in a `more` like window overlaid on the editor.
|
||||||
*/
|
*/
|
||||||
class OutputPanel(editorRef: WeakReference<Editor>) : JBPanel<OutputPanel?>(), VimOutputPanel {
|
class OutputPanel private constructor(
|
||||||
private val myEditorRef: WeakReference<Editor> = editorRef
|
private val editor: Editor,
|
||||||
val editor: Editor? get() = myEditorRef.get()
|
) : JBPanel<OutputPanel>(), VimOutputPanel {
|
||||||
|
|
||||||
val myLabel: JLabel = JLabel("more")
|
private val textPane = JTextPane()
|
||||||
private val myText = JTextArea()
|
private val resizeAdapter: ComponentAdapter
|
||||||
private val myScrollPane: JScrollPane =
|
private var defaultForeground: Color? = null
|
||||||
JBScrollPane(myText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
|
|
||||||
private val myAdapter: ComponentAdapter
|
|
||||||
private var myLineHeight = 0
|
|
||||||
|
|
||||||
private var myOldGlass: JComponent? = null
|
private var glassPane: JComponent? = null
|
||||||
private var myOldLayout: LayoutManager? = null
|
private var originalLayout: LayoutManager? = null
|
||||||
private var myWasOpaque = false
|
private var wasOpaque = false
|
||||||
|
|
||||||
var myActive: Boolean = false
|
var active: Boolean = false
|
||||||
|
private val segments = mutableListOf<TextLine>()
|
||||||
|
|
||||||
val isActive: Boolean
|
private val labelComponent: JLabel = JLabel("more")
|
||||||
get() = myActive
|
private val scrollPane: JScrollPane =
|
||||||
|
JBScrollPane(textPane, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
|
||||||
|
private var cachedLineHeight = 0
|
||||||
|
private var isSingleLine = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Create a text editor for the text and a label for the prompt
|
textPane.isEditable = false
|
||||||
val layout = BorderLayout(0, 0)
|
textPane.caret = object : DefaultCaret() {
|
||||||
setLayout(layout)
|
override fun setVisible(v: Boolean) {
|
||||||
add(myScrollPane, BorderLayout.CENTER)
|
super.setVisible(false)
|
||||||
add(myLabel, BorderLayout.SOUTH)
|
}
|
||||||
|
}
|
||||||
|
textPane.highlighter = null
|
||||||
|
|
||||||
// Set the text area read only, and support wrap
|
resizeAdapter = object : ComponentAdapter() {
|
||||||
myText.isEditable = false
|
|
||||||
myText.setLineWrap(true)
|
|
||||||
|
|
||||||
myAdapter = object : ComponentAdapter() {
|
|
||||||
override fun componentResized(e: ComponentEvent?) {
|
override fun componentResized(e: ComponentEvent?) {
|
||||||
positionPanel()
|
positionPanel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup some listeners to handle keystrokes
|
// Suppress the fancy frame background used in the Islands theme
|
||||||
val moreKeyListener = MoreKeyListener()
|
ClientProperty.putRecursive(this, IdeBackgroundUtil.NO_BACKGROUND, true)
|
||||||
addKeyListener(moreKeyListener)
|
putClientProperty(ToolWindowManagerImpl.PARENT_COMPONENT, editor.component)
|
||||||
myText.addKeyListener(moreKeyListener)
|
|
||||||
|
|
||||||
// Suppress the fancy frame background used in the Islands theme, which comes from a custom Graphics implementation
|
// Initialize panel
|
||||||
// applied to the IdeRoot, and used to paint all children, including this panel. This client property is checked by
|
setLayout(BorderLayout(0, 0))
|
||||||
// JBPanel.getComponentGraphics to give us the original Graphics, opting out of the fancy painting.
|
add(scrollPane, BorderLayout.CENTER)
|
||||||
ClientProperty.putRecursive<Boolean?>(this, IdeBackgroundUtil.NO_BACKGROUND, true)
|
add(labelComponent, BorderLayout.SOUTH)
|
||||||
editor?.let { putClientProperty(ToolWindowManagerImpl.PARENT_COMPONENT, it.component) }
|
|
||||||
|
val keyListener = OutputPanelKeyListener()
|
||||||
|
addKeyListener(keyListener)
|
||||||
|
textPane.addKeyListener(keyListener)
|
||||||
|
|
||||||
updateUI()
|
updateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called automatically when the LAF is changed and the component is visible, and manually by the LAF listener handler
|
|
||||||
override fun updateUI() {
|
override fun updateUI() {
|
||||||
super.updateUI()
|
super.updateUI()
|
||||||
|
|
||||||
setBorder(ExPanelBorder())
|
setBorder(ExPanelBorder())
|
||||||
|
|
||||||
// Swing uses a bad pattern of calling updateUI() from the constructor. At this moment, all these variables are null
|
|
||||||
@Suppress("SENSELESS_COMPARISON")
|
@Suppress("SENSELESS_COMPARISON")
|
||||||
if (myText != null && myLabel != null && myScrollPane != null) {
|
if (textPane != null && labelComponent != null && scrollPane != null) {
|
||||||
setFontForElements()
|
setFontForElements()
|
||||||
myText.setBorder(null)
|
textPane.setBorder(null)
|
||||||
myScrollPane.setBorder(null)
|
scrollPane.setBorder(null)
|
||||||
myLabel.setForeground(myText.getForeground())
|
labelComponent.setForeground(textPane.getForeground())
|
||||||
|
|
||||||
// Make sure the panel is positioned correctly in case we're changing font size
|
|
||||||
positionPanel()
|
positionPanel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override var text: String
|
override var text: String
|
||||||
get() = myText.text
|
get() = textPane.getText() ?: ""
|
||||||
set(value) {
|
set(value) {
|
||||||
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour.
|
|
||||||
val newValue = value.removeSuffix("\n")
|
val newValue = value.removeSuffix("\n")
|
||||||
myText.text = newValue
|
segments.clear()
|
||||||
val ed = editor
|
if (newValue.isEmpty()) return
|
||||||
if (ed != null) {
|
segments.add(TextLine(newValue, null))
|
||||||
myText.setFont(selectEditorFont(ed, newValue))
|
|
||||||
}
|
|
||||||
myText.setCaretPosition(0)
|
|
||||||
if (newValue.isNotEmpty()) {
|
|
||||||
activate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override var label: String
|
override var label: String
|
||||||
get() = myLabel.text ?: ""
|
get() = labelComponent.text
|
||||||
set(value) {
|
set(value) {
|
||||||
myLabel.text = value
|
labelComponent.text = value
|
||||||
val ed = editor
|
|
||||||
if (ed != null) {
|
|
||||||
myLabel.setFont(selectEditorFont(ed, value))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addText(text: String, isNewLine: Boolean, messageType: MessageType) {
|
/**
|
||||||
if (this.text.isNotEmpty() && isNewLine) {
|
* Sets styled text with multiple segments, each potentially having a different color.
|
||||||
this.text += "\n$text"
|
*/
|
||||||
|
fun setStyledText(lines: List<TextLine>) {
|
||||||
|
val doc = textPane.styledDocument
|
||||||
|
doc.remove(0, doc.length)
|
||||||
|
|
||||||
|
if (defaultForeground == null) {
|
||||||
|
defaultForeground = textPane.foreground
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines.size > 1) {
|
||||||
|
setMultiLineText(lines, doc)
|
||||||
} else {
|
} else {
|
||||||
this.text += text
|
doc.insertString(doc.length, lines[0].text.removeSuffix("\n"), getLineColor(lines[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
val fullText = doc.getText(0, doc.length)
|
||||||
|
textPane.setFont(selectEditorFont(editor, fullText))
|
||||||
|
textPane.setCaretPosition(0)
|
||||||
|
if (fullText.isNotEmpty()) {
|
||||||
|
activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setMultiLineText(
|
||||||
|
lines: List<TextLine>,
|
||||||
|
doc: StyledDocument,
|
||||||
|
) {
|
||||||
|
for ((index, line) in lines.withIndex()) {
|
||||||
|
val text = line.text.removeSuffix("\n")
|
||||||
|
val attrs = getLineColor(line)
|
||||||
|
val separator = if (index < lines.size - 1) "\n" else ""
|
||||||
|
doc.insertString(doc.length, text + separator, attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLineColor(segment: TextLine): SimpleAttributeSet {
|
||||||
|
val attrs = SimpleAttributeSet()
|
||||||
|
val color = segment.color ?: defaultForeground
|
||||||
|
if (color != null) {
|
||||||
|
StyleConstants.setForeground(attrs, color)
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addText(text: String, isNewLine: Boolean, messageType: MessageType) {
|
||||||
|
val color = when (messageType) {
|
||||||
|
MessageType.ERROR -> JBColor.RED
|
||||||
|
MessageType.STANDARD -> null
|
||||||
|
}
|
||||||
|
segments.add(TextLine(text, color))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
|
||||||
|
if (currentPanel != null && currentPanel != this) currentPanel.close()
|
||||||
|
|
||||||
|
setStyledText(segments)
|
||||||
|
if (!active) {
|
||||||
|
activate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,20 +204,15 @@ class OutputPanel(editorRef: WeakReference<Editor>) : JBPanel<OutputPanel?>(), V
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun clearText() {
|
override fun clearText() {
|
||||||
|
segments.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
text = ""
|
text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun show() {
|
|
||||||
editor ?: return
|
|
||||||
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
|
|
||||||
if (currentPanel != null && currentPanel != this) currentPanel.close()
|
|
||||||
|
|
||||||
if (!myActive) {
|
|
||||||
activate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleKey(key: KeyStroke) {
|
override fun handleKey(key: KeyStroke) {
|
||||||
|
|
||||||
if (isAtEnd) {
|
if (isAtEnd) {
|
||||||
close(key)
|
close(key)
|
||||||
return
|
return
|
||||||
@@ -197,214 +237,262 @@ class OutputPanel(editorRef: WeakReference<Editor>) : JBPanel<OutputPanel?>(), V
|
|||||||
|
|
||||||
override fun getForeground(): Color? {
|
override fun getForeground(): Color? {
|
||||||
@Suppress("SENSELESS_COMPARISON")
|
@Suppress("SENSELESS_COMPARISON")
|
||||||
if (myText == null) {
|
if (textPane == null) {
|
||||||
// Swing uses a bad pattern of calling getForeground() from the constructor. At this moment, `myText` is null.
|
|
||||||
return super.getForeground()
|
return super.getForeground()
|
||||||
}
|
}
|
||||||
return myText.getForeground()
|
return textPane.getForeground()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBackground(): Color? {
|
override fun getBackground(): Color? {
|
||||||
@Suppress("SENSELESS_COMPARISON")
|
@Suppress("SENSELESS_COMPARISON")
|
||||||
if (myText == null) {
|
if (textPane == null) {
|
||||||
// Swing uses a bad pattern of calling getBackground() from the constructor. At this moment, `myText` is null.
|
|
||||||
return super.getBackground()
|
return super.getBackground()
|
||||||
}
|
}
|
||||||
return myText.getBackground()
|
return textPane.getBackground()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns off the ex entry field and optionally puts the focus back to the original component
|
* Turns off the output panel and optionally puts the focus back to the original component.
|
||||||
*/
|
*/
|
||||||
fun deactivate(refocusOwningEditor: Boolean) {
|
fun deactivate(refocusOwningEditor: Boolean) {
|
||||||
if (!myActive) return
|
if (!active) return
|
||||||
myActive = false
|
active = false
|
||||||
myText.text = ""
|
clearText()
|
||||||
val ed = editor
|
textPane.text = ""
|
||||||
if (refocusOwningEditor && ed != null) {
|
if (refocusOwningEditor) {
|
||||||
requestFocus(ed.contentComponent)
|
requestFocus(editor.contentComponent)
|
||||||
}
|
}
|
||||||
if (myOldGlass != null) {
|
if (glassPane != null) {
|
||||||
myOldGlass!!.removeComponentListener(myAdapter)
|
glassPane!!.removeComponentListener(resizeAdapter)
|
||||||
myOldGlass!!.isVisible = false
|
glassPane!!.isVisible = false
|
||||||
myOldGlass!!.remove(this)
|
glassPane!!.remove(this)
|
||||||
myOldGlass!!.setOpaque(myWasOpaque)
|
glassPane!!.setOpaque(wasOpaque)
|
||||||
myOldGlass!!.setLayout(myOldLayout)
|
glassPane!!.setLayout(originalLayout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns on the more window for the given editor
|
* Turns on the output panel for the given editor.
|
||||||
*/
|
*/
|
||||||
fun activate() {
|
fun activate() {
|
||||||
val ed = editor ?: return
|
disableOldGlass()
|
||||||
val root = SwingUtilities.getRootPane(ed.contentComponent)
|
|
||||||
deactivateOldGlass(root)
|
|
||||||
|
|
||||||
setFontForElements()
|
setFontForElements()
|
||||||
positionPanel()
|
positionPanel()
|
||||||
|
|
||||||
if (myOldGlass != null) {
|
if (glassPane != null) {
|
||||||
myOldGlass!!.isVisible = true
|
glassPane!!.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
myActive = true
|
active = true
|
||||||
requestFocus(myText)
|
requestFocus(textPane)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deactivateOldGlass(root: JRootPane?) {
|
private fun disableOldGlass() {
|
||||||
if (root == null) return
|
val root = SwingUtilities.getRootPane(editor.contentComponent) ?: return
|
||||||
myOldGlass = root.getGlassPane() as JComponent?
|
glassPane = root.getGlassPane() as JComponent?
|
||||||
if (myOldGlass != null) {
|
if (glassPane == null) {
|
||||||
myOldLayout = myOldGlass!!.layout
|
|
||||||
myWasOpaque = myOldGlass!!.isOpaque
|
|
||||||
myOldGlass!!.setLayout(null)
|
|
||||||
myOldGlass!!.setOpaque(false)
|
|
||||||
myOldGlass!!.add(this)
|
|
||||||
myOldGlass!!.addComponentListener(myAdapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setFontForElements() {
|
|
||||||
val ed = editor ?: return
|
|
||||||
myText.setFont(selectEditorFont(ed, myText.getText()))
|
|
||||||
myLabel.setFont(selectEditorFont(ed, myLabel.text))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun scrollLine() {
|
|
||||||
scrollOffset(myLineHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun scrollPage() {
|
|
||||||
scrollOffset(myScrollPane.getVerticalScrollBar().visibleAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun scrollHalfPage() {
|
|
||||||
val sa = myScrollPane.getVerticalScrollBar().visibleAmount / 2.0
|
|
||||||
val offset = ceil(sa / myLineHeight) * myLineHeight
|
|
||||||
scrollOffset(offset.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onBadKey() {
|
|
||||||
val ed = editor ?: return
|
|
||||||
myLabel.setText(injector.messages.message("message.ex.output.more.prompt.full"))
|
|
||||||
myLabel.setFont(selectEditorFont(ed, myLabel.text))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scrollOffset(more: Int) {
|
|
||||||
val ed = editor ?: return
|
|
||||||
val `val` = myScrollPane.getVerticalScrollBar().value
|
|
||||||
myScrollPane.getVerticalScrollBar().setValue(`val` + more)
|
|
||||||
myScrollPane.getHorizontalScrollBar().setValue(0)
|
|
||||||
if (isAtEnd) {
|
|
||||||
myLabel.setText(injector.messages.message("message.ex.output.end.prompt"))
|
|
||||||
} else {
|
|
||||||
myLabel.setText(injector.messages.message("message.ex.output.more.prompt"))
|
|
||||||
}
|
|
||||||
myLabel.setFont(selectEditorFont(ed, myLabel.text))
|
|
||||||
}
|
|
||||||
|
|
||||||
val isAtEnd: Boolean
|
|
||||||
get() {
|
|
||||||
val isSingleLine = myText.getLineCount() == 1
|
|
||||||
if (isSingleLine) return true
|
|
||||||
val scrollBar = myScrollPane.getVerticalScrollBar()
|
|
||||||
val value = scrollBar.value
|
|
||||||
if (!scrollBar.isVisible) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return value >= scrollBar.maximum - scrollBar.visibleAmount ||
|
|
||||||
scrollBar.maximum <= scrollBar.visibleAmount
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun positionPanel() {
|
|
||||||
val ed = editor ?: return
|
|
||||||
val contentComponent = ed.contentComponent
|
|
||||||
val scroll = SwingUtilities.getAncestorOfClass(JScrollPane::class.java, contentComponent)
|
|
||||||
val rootPane = SwingUtilities.getRootPane(contentComponent)
|
|
||||||
if (scroll == null || rootPane == null) {
|
|
||||||
// These might be null if we're invoked during component initialisation and before it's been added to the tree
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
originalLayout = glassPane!!.layout
|
||||||
size = scroll.size
|
wasOpaque = glassPane!!.isOpaque
|
||||||
|
glassPane!!.setLayout(null)
|
||||||
myLineHeight = myText.getFontMetrics(myText.getFont()).height
|
glassPane!!.setOpaque(false)
|
||||||
val count: Int = countLines(myText.getText())
|
glassPane!!.add(this)
|
||||||
val visLines = size.height / myLineHeight - 1
|
glassPane!!.addComponentListener(resizeAdapter)
|
||||||
val lines = min(count, visLines)
|
|
||||||
setSize(
|
|
||||||
size.width,
|
|
||||||
lines * myLineHeight + myLabel.getPreferredSize().height + border.getBorderInsets(this).top * 2
|
|
||||||
)
|
|
||||||
|
|
||||||
val height = size.height
|
|
||||||
val bounds = scroll.bounds
|
|
||||||
bounds.translate(0, scroll.getHeight() - height)
|
|
||||||
bounds.height = height
|
|
||||||
val pos = SwingUtilities.convertPoint(scroll.getParent(), bounds.location, rootPane.getGlassPane())
|
|
||||||
bounds.location = pos
|
|
||||||
setBounds(bounds)
|
|
||||||
|
|
||||||
myScrollPane.getVerticalScrollBar().setValue(0)
|
|
||||||
if (!injector.globalOptions().more) {
|
|
||||||
// FIX
|
|
||||||
scrollOffset(100000)
|
|
||||||
} else {
|
|
||||||
scrollOffset(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun close(key: KeyStroke? = null) {
|
|
||||||
val ed = editor ?: return
|
|
||||||
ApplicationManager.getApplication().invokeLater {
|
|
||||||
deactivate(true)
|
|
||||||
val project = ed.project
|
|
||||||
if (project != null && key != null && key.keyChar != '\n') {
|
|
||||||
val keys: MutableList<KeyStroke> = ArrayList(1)
|
|
||||||
keys.add(key)
|
|
||||||
if (LOG.isTrace()) {
|
|
||||||
LOG.trace(
|
|
||||||
"Adding new keys to keyStack as part of playback. State before adding keys: " +
|
|
||||||
getInstance().keyStack.dump()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
getInstance().keyStack.addKeys(keys)
|
|
||||||
val context: ExecutionContext =
|
|
||||||
injector.executionContextManager.getEditorExecutionContext(IjVimEditor(ed))
|
|
||||||
VimPlugin.getMacro().playbackKeys(IjVimEditor(ed), context, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
close(null)
|
close(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MoreKeyListener : KeyAdapter() {
|
fun close(key: KeyStroke?) {
|
||||||
/**
|
val passKeyBack = isSingleLine
|
||||||
* Invoked when a key has been pressed.
|
ApplicationManager.getApplication().invokeLater {
|
||||||
*/
|
deactivate(true)
|
||||||
|
val project = editor.project
|
||||||
|
// For single line messages, pass any key back to the editor (including Enter)
|
||||||
|
// For multi-line messages, don't pass Enter back (it was used to dismiss)
|
||||||
|
if (project != null && key != null && (passKeyBack || key.keyChar != '\n')) {
|
||||||
|
val keys: MutableList<KeyStroke> = ArrayList(1)
|
||||||
|
keys.add(key)
|
||||||
|
getInstance().keyStack.addKeys(keys)
|
||||||
|
val context: ExecutionContext =
|
||||||
|
injector.executionContextManager.getEditorExecutionContext(IjVimEditor(editor))
|
||||||
|
VimPlugin.getMacro().playbackKeys(IjVimEditor(editor), context, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setFontForElements() {
|
||||||
|
textPane.setFont(selectEditorFont(editor, textPane.getText()))
|
||||||
|
labelComponent.setFont(selectEditorFont(editor, labelComponent.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun positionPanel() {
|
||||||
|
val scroll = positionPanelStart() ?: return
|
||||||
|
val lineHeight = textPane.getFontMetrics(textPane.getFont()).height
|
||||||
|
val count = countLines(textPane.getText())
|
||||||
|
val visLines = size.height / lineHeight - 1
|
||||||
|
val lines = min(count, visLines)
|
||||||
|
|
||||||
|
// Simple output: single line that fits entirely - no label needed
|
||||||
|
isSingleLine = count == 1 && count <= visLines
|
||||||
|
labelComponent.isVisible = !isSingleLine
|
||||||
|
|
||||||
|
val extraHeight = if (isSingleLine) 0 else labelComponent.getPreferredSize().height
|
||||||
|
setSize(
|
||||||
|
size.width,
|
||||||
|
lines * lineHeight + extraHeight + border.getBorderInsets(this).top * 2
|
||||||
|
)
|
||||||
|
|
||||||
|
finishPositioning(scroll)
|
||||||
|
|
||||||
|
// Force layout so that viewport sizes are valid before checking scroll state
|
||||||
|
validate()
|
||||||
|
|
||||||
|
// onPositioned
|
||||||
|
cachedLineHeight = lineHeight
|
||||||
|
scrollPane.getVerticalScrollBar().setValue(0)
|
||||||
|
if (!isSingleLine) {
|
||||||
|
if (!injector.globalOptions().more) {
|
||||||
|
scrollOffset(100000)
|
||||||
|
} else {
|
||||||
|
scrollOffset(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun positionPanelStart(): JScrollPane? {
|
||||||
|
val contentComponent = editor.contentComponent
|
||||||
|
val scroll = SwingUtilities.getAncestorOfClass(JScrollPane::class.java, contentComponent) as? JScrollPane
|
||||||
|
val rootPane = SwingUtilities.getRootPane(contentComponent)
|
||||||
|
if (scroll == null || rootPane == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
size = scroll.size
|
||||||
|
return scroll
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun finishPositioning(scroll: JScrollPane) {
|
||||||
|
val rootPane = SwingUtilities.getRootPane(editor.contentComponent)
|
||||||
|
val bounds = scroll.bounds
|
||||||
|
bounds.translate(0, scroll.getHeight() - size.height)
|
||||||
|
bounds.height = size.height
|
||||||
|
val pos = SwingUtilities.convertPoint(scroll.getParent(), bounds.location, rootPane.getGlassPane())
|
||||||
|
bounds.location = pos
|
||||||
|
setBounds(bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun countLines(text: String): Int {
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
var pos = -1
|
||||||
|
while ((text.indexOf('\n', pos + 1).also { pos = it }) != -1) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text[text.length - 1] != '\n') {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun scrollLine() {
|
||||||
|
scrollOffset(cachedLineHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun scrollPage() {
|
||||||
|
scrollOffset(scrollPane.getVerticalScrollBar().visibleAmount)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun scrollHalfPage() {
|
||||||
|
val sa = scrollPane.getVerticalScrollBar().visibleAmount / 2.0
|
||||||
|
val offset = ceil(sa / cachedLineHeight) * cachedLineHeight
|
||||||
|
scrollOffset(offset.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBadKey() {
|
||||||
|
labelComponent.setText(injector.messages.message("message.ex.output.more.prompt.full"))
|
||||||
|
labelComponent.setFont(selectEditorFont(editor, labelComponent.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun scrollOffset(more: Int) {
|
||||||
|
scrollPane.validate()
|
||||||
|
val scrollBar = scrollPane.getVerticalScrollBar()
|
||||||
|
val value = scrollBar.value
|
||||||
|
scrollBar.setValue(value + more)
|
||||||
|
scrollPane.getHorizontalScrollBar().setValue(0)
|
||||||
|
|
||||||
|
// Check if we're at the end or if content fits entirely (nothing to scroll)
|
||||||
|
if (isAtEnd) {
|
||||||
|
labelComponent.setText(injector.messages.message("message.ex.output.end.prompt"))
|
||||||
|
} else {
|
||||||
|
labelComponent.setText(injector.messages.message("message.ex.output.more.prompt"))
|
||||||
|
}
|
||||||
|
labelComponent.setFont(selectEditorFont(editor, labelComponent.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
val isAtEnd: Boolean
|
||||||
|
get() {
|
||||||
|
if (isSingleLine) return true
|
||||||
|
val contentHeight = textPane.preferredSize.height
|
||||||
|
val viewportHeight = scrollPane.viewport.height
|
||||||
|
if (contentHeight <= viewportHeight) return true
|
||||||
|
val scrollBar = scrollPane.getVerticalScrollBar()
|
||||||
|
return scrollBar.value >= scrollBar.maximum - scrollBar.visibleAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class OutputPanelKeyListener : KeyAdapter() {
|
||||||
override fun keyTyped(e: KeyEvent) {
|
override fun keyTyped(e: KeyEvent) {
|
||||||
val currentPanel: VimOutputPanel = injector.outputPanel.getCurrentOutputPanel() ?: return
|
val currentPanel: VimOutputPanel = injector.outputPanel.getCurrentOutputPanel() ?: return
|
||||||
|
|
||||||
val keyCode = e.getKeyCode()
|
val keyChar = e.keyChar
|
||||||
val keyChar = e.getKeyChar()
|
|
||||||
val modifiers = e.modifiersEx
|
val modifiers = e.modifiersEx
|
||||||
val keyStroke = if (keyChar == KeyEvent.CHAR_UNDEFINED)
|
val keyStroke = KeyStroke.getKeyStroke(keyChar, modifiers)
|
||||||
KeyStroke.getKeyStroke(keyCode, modifiers)
|
|
||||||
else
|
|
||||||
KeyStroke.getKeyStroke(keyChar, modifiers)
|
|
||||||
currentPanel.handleKey(keyStroke)
|
currentPanel.handleKey(keyStroke)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun keyPressed(e: KeyEvent) {
|
||||||
|
if (!e.isActionKey && e.keyCode != KeyEvent.VK_ENTER) return
|
||||||
|
val currentPanel = injector.outputPanel.getCurrentOutputPanel() as? OutputPanel ?: return
|
||||||
|
|
||||||
|
val keyCode = e.keyCode
|
||||||
|
val modifiers = e.modifiersEx
|
||||||
|
val keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers)
|
||||||
|
|
||||||
|
if (isSingleLine) {
|
||||||
|
currentPanel.close(keyStroke)
|
||||||
|
e.consume()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-line mode: arrow keys scroll, down/right at end closes
|
||||||
|
when (keyCode) {
|
||||||
|
KeyEvent.VK_ENTER -> {
|
||||||
|
if (currentPanel.isAtEnd) currentPanel.close() else currentPanel.scrollLine()
|
||||||
|
e.consume()
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEvent.VK_DOWN -> if (currentPanel.isAtEnd) currentPanel.close(keyStroke) else currentPanel.scrollLine()
|
||||||
|
KeyEvent.VK_RIGHT -> if (currentPanel.isAtEnd) currentPanel.close(keyStroke) else currentPanel.scrollLine()
|
||||||
|
KeyEvent.VK_UP -> currentPanel.scrollOffset(-cachedLineHeight)
|
||||||
|
KeyEvent.VK_LEFT -> currentPanel.scrollOffset(-cachedLineHeight)
|
||||||
|
KeyEvent.VK_PAGE_DOWN -> if (currentPanel.isAtEnd) currentPanel.close(keyStroke) else currentPanel.scrollPage()
|
||||||
|
KeyEvent.VK_PAGE_UP -> currentPanel.scrollOffset(-scrollPane.verticalScrollBar.visibleAmount)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LafListener : LafManagerListener {
|
class LafListener : LafManagerListener {
|
||||||
override fun lookAndFeelChanged(source: LafManager) {
|
override fun lookAndFeelChanged(source: LafManager) {
|
||||||
if (VimPlugin.isNotEnabled()) return
|
if (VimPlugin.isNotEnabled()) return
|
||||||
|
|
||||||
// This listener is only invoked for local scenarios, and we only need to update local editor UI. This will invoke
|
|
||||||
// updateUI on the output pane and it's child components
|
|
||||||
for (vimEditor in injector.editorGroup.getEditors()) {
|
for (vimEditor in injector.editorGroup.getEditors()) {
|
||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
if (!isPanelActive(editor)) continue
|
if (!isPanelActive(editor)) continue
|
||||||
@@ -414,41 +502,24 @@ class OutputPanel(editorRef: WeakReference<Editor>) : JBPanel<OutputPanel?>(), V
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG: VimLogger = injector.getLogger<OutputPanel>(OutputPanel::class.java)
|
|
||||||
|
|
||||||
fun getNullablePanel(editor: Editor): OutputPanel? {
|
fun getNullablePanel(editor: Editor): OutputPanel? {
|
||||||
return editor.vimMorePanel as? OutputPanel
|
return editor.vimMorePanel as OutputPanel?
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPanelActive(editor: Editor): Boolean {
|
fun isPanelActive(editor: Editor): Boolean {
|
||||||
return getNullablePanel(editor)?.myActive ?: false
|
return getNullablePanel(editor) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInstance(editor: Editor): OutputPanel {
|
fun getInstance(editor: Editor): OutputPanel {
|
||||||
var panel: OutputPanel? = getNullablePanel(editor)
|
var panel: OutputPanel? = getNullablePanel(editor)
|
||||||
if (panel == null) {
|
if (panel == null) {
|
||||||
panel = OutputPanel(WeakReference(editor))
|
panel = OutputPanel(editor)
|
||||||
editor.vimMorePanel = panel
|
editor.vimMorePanel = panel
|
||||||
}
|
}
|
||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun countLines(text: String): Int {
|
|
||||||
if (text.isEmpty()) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = 0
|
|
||||||
var pos = -1
|
|
||||||
while ((text.indexOf('\n', pos + 1).also { pos = it }) != -1) {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text[text.length - 1] != '\n') {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class TextLine(val text: String, val color: Color?)
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class ExTextField internal constructor(private val myParentPanel: ExEntryPanel)
|
|||||||
// handler adds all non-control characters to the text field. We want to add all characters, so if we have an
|
// handler adds all non-control characters to the text field. We want to add all characters, so if we have an
|
||||||
// actual character, just add it. Anything else, we'll pass to the super class like before (even though it's unclear
|
// actual character, just add it. Anything else, we'll pass to the super class like before (even though it's unclear
|
||||||
// what it will do with the keystroke)
|
// what it will do with the keystroke)
|
||||||
if (stroke.keyChar != KeyEvent.CHAR_UNDEFINED && !isKeyCharEnterOrEscape(stroke.keyChar)) {
|
if (stroke.keyChar != KeyEvent.CHAR_UNDEFINED) {
|
||||||
replaceSelection(stroke.keyChar.toString())
|
replaceSelection(stroke.keyChar.toString())
|
||||||
} else {
|
} else {
|
||||||
val event = KeyEvent(
|
val event = KeyEvent(
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ class IjOutputPanelService : VimOutputPanelServiceBase() {
|
|||||||
private var activeOutputPanel: WeakReference<VimOutputPanel>? = null
|
private var activeOutputPanel: WeakReference<VimOutputPanel>? = null
|
||||||
|
|
||||||
override fun getCurrentOutputPanel(): VimOutputPanel? {
|
override fun getCurrentOutputPanel(): VimOutputPanel? {
|
||||||
return activeOutputPanel?.get()?.takeIf { (it as OutputPanel).isActive }
|
return activeOutputPanel?.get()?.takeIf { (it as OutputPanel).active }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
|
override fun create(editor: VimEditor, context: ExecutionContext): VimOutputPanel {
|
||||||
val panel = OutputPanel(WeakReference(editor.ij))
|
val panel = OutputPanel.getInstance(editor.ij)
|
||||||
activeOutputPanel = WeakReference(panel)
|
activeOutputPanel = WeakReference(panel)
|
||||||
return panel
|
return panel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.maddyhome.idea.vim.vimscript.model.functions.handlers
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
|
||||||
import com.intellij.openapi.actionSystem.impl.PresentationFactory
|
|
||||||
import com.intellij.openapi.actionSystem.impl.Utils
|
|
||||||
import com.intellij.openapi.keymap.impl.ActionProcessor
|
|
||||||
import com.intellij.vim.annotations.VimscriptFunction
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.asVimInt
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.functions.BuiltinFunctionHandler
|
|
||||||
import java.awt.event.KeyEvent
|
|
||||||
|
|
||||||
@VimscriptFunction(name = "isactionenabled")
|
|
||||||
internal class IsActionEnabled : BuiltinFunctionHandler<VimInt>() {
|
|
||||||
override fun doFunction(
|
|
||||||
arguments: Arguments,
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
vimContext: VimLContext,
|
|
||||||
): VimInt {
|
|
||||||
val action = ActionManager.getInstance().getAction(arguments.getString(0).value)
|
|
||||||
if (action == null) {
|
|
||||||
return false.asVimInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
val presentationFactory = PresentationFactory()
|
|
||||||
val wrappedContext = Utils.createAsyncDataContext(context.ij)
|
|
||||||
val actionProcessor = object : ActionProcessor() {}
|
|
||||||
val inputEventAdjusted = KeyEvent(editor.ij.contentComponent, KeyEvent.KEY_PRESSED, 0L, 0, KeyEvent.VK_UNDEFINED, '\u0000')
|
|
||||||
|
|
||||||
val updateEvent = Utils.runUpdateSessionForInputEvent(listOf(action), inputEventAdjusted, wrappedContext, "IdeaVim", actionProcessor, presentationFactory) { _, updater, events ->
|
|
||||||
val presentation = updater(action)
|
|
||||||
events[presentation]
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = updateEvent != null && updateEvent.presentation.isEnabled
|
|
||||||
return result.asVimInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<!--
|
|
||||||
~ Copyright 2003-2026 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- Split-mode (Remote Dev) specific registrations.
|
|
||||||
This module only loads when intellij.platform.frontend.split is available,
|
|
||||||
which provides access to intellij.rd.client and its extension points. -->
|
|
||||||
<idea-plugin package="com.maddyhome.idea.vim">
|
|
||||||
<dependencies>
|
|
||||||
<module name="intellij.platform.frontend.split"/>
|
|
||||||
<module name="IdeaVIM.ideavim-frontend"/>
|
|
||||||
</dependencies>
|
|
||||||
</idea-plugin>
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler",
|
"has": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.HasFunctionHandler",
|
||||||
"isactionenabled": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.IsActionEnabled",
|
|
||||||
"pumvisible": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.PopupMenuVisibleFunctionHandler"
|
"pumvisible": "com.maddyhome.idea.vim.vimscript.model.functions.handlers.PopupMenuVisibleFunctionHandler"
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2026 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -10,12 +10,9 @@ package org.jetbrains.plugins.ideavim.action.motion.search
|
|||||||
|
|
||||||
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.ui.ex.ExEntryPanel
|
|
||||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
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 kotlin.test.assertFalse
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
class SearchEntryFwdActionTest : VimTestCase() {
|
class SearchEntryFwdActionTest : VimTestCase() {
|
||||||
@Test
|
@Test
|
||||||
@@ -113,23 +110,6 @@ class SearchEntryFwdActionTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `test escape after search not found closes panel without inserting escape char`() {
|
|
||||||
configureByText("lorem ipsum dolor sit amet")
|
|
||||||
typeText("/notfound")
|
|
||||||
|
|
||||||
val panel = ExEntryPanel.getOrCreatePanelInstance()
|
|
||||||
assertTrue(panel.isActive)
|
|
||||||
|
|
||||||
typeText("<Esc>")
|
|
||||||
|
|
||||||
assertFalse(panel.isActive)
|
|
||||||
assertMode(Mode.NORMAL())
|
|
||||||
// The panel text should not contain ^[ (escape character written as text)
|
|
||||||
assertFalse(panel.text.contains("\u001B"), "Panel text should not contain escape character")
|
|
||||||
assertFalse(panel.text.contains("^["), "Panel text should not contain ^[ literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Disabled("Ctrl-o doesn't work yet in select mode")
|
@Disabled("Ctrl-o doesn't work yet in select mode")
|
||||||
@Test
|
@Test
|
||||||
fun `search in one time from select mode`() {
|
fun `search in one time from select mode`() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -134,7 +134,7 @@ class CmdCommandTest : VimTestCase() {
|
|||||||
VimPlugin.getCommand().resetAliases()
|
VimPlugin.getCommand().resetAliases()
|
||||||
configureByText("\n")
|
configureByText("\n")
|
||||||
typeText(commandToKeys("command! -range Error echo <args>"))
|
typeText(commandToKeys("command! -range Error echo <args>"))
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
kotlin.test.assertEquals("'-range' is not supported by `command`", injector.messages.getStatusBarMessage())
|
kotlin.test.assertEquals("'-range' is not supported by `command`", injector.messages.getStatusBarMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ class CmdCommandTest : VimTestCase() {
|
|||||||
VimPlugin.getCommand().resetAliases()
|
VimPlugin.getCommand().resetAliases()
|
||||||
configureByText("\n")
|
configureByText("\n")
|
||||||
typeText(commandToKeys("command! -complete=color Error echo <args>"))
|
typeText(commandToKeys("command! -complete=color Error echo <args>"))
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
kotlin.test.assertEquals("'-complete' is not supported by `command`", injector.messages.getStatusBarMessage())
|
kotlin.test.assertEquals("'-complete' is not supported by `command`", injector.messages.getStatusBarMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -24,7 +24,6 @@ class ExecuteCommandTest : VimTestCase() {
|
|||||||
fun `test execute with range`() {
|
fun `test execute with range`() {
|
||||||
configureByText("\n")
|
configureByText("\n")
|
||||||
typeText(commandToKeys("1,2execute 'echo 42'"))
|
typeText(commandToKeys("1,2execute 'echo 42'"))
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ class HistoryCommandTest : VimTestCase() {
|
|||||||
fun `test history with 'history' option set to 0 shows nothing`() {
|
fun `test history with 'history' option set to 0 shows nothing`() {
|
||||||
enterCommand("set history=0")
|
enterCommand("set history=0")
|
||||||
enterCommand("history")
|
enterCommand("history")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(false)
|
assertPluginError(false)
|
||||||
assertPluginErrorMessage("'history' option is zero")
|
assertPluginErrorMessage("'history' option is zero")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -43,7 +43,6 @@ class AndFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test and function with list causes error`() {
|
fun `test and function with list causes error`() {
|
||||||
enterCommand("echo and([1, 2, 3], [2, 3, 4])")
|
enterCommand("echo and([1, 2, 3], [2, 3, 4])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E745: Using a List as a Number")
|
assertPluginErrorMessage("E745: Using a List as a Number")
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,6 @@ class AndFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test and function with dict causes error`() {
|
fun `test and function with dict causes error`() {
|
||||||
enterCommand("echo and({1: 2, 3: 4}, {3: 4, 5: 6})")
|
enterCommand("echo and({1: 2, 3: 4}, {3: 4, 5: 6})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
||||||
}
|
}
|
||||||
@@ -59,7 +57,6 @@ class AndFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test and function with float causes error`() {
|
fun `test and function with float causes error`() {
|
||||||
enterCommand("echo and(1.5, 2.5)")
|
enterCommand("echo and(1.5, 2.5)")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E805: Using a Float as a Number")
|
assertPluginErrorMessage("E805: Using a Float as a Number")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -28,7 +28,6 @@ class InvertFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test invert function with list causes error`() {
|
fun `test invert function with list causes error`() {
|
||||||
enterCommand("echo invert([1, 2, 3])")
|
enterCommand("echo invert([1, 2, 3])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E745: Using a List as a Number")
|
assertPluginErrorMessage("E745: Using a List as a Number")
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,6 @@ class InvertFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test invert function with dict causes error`() {
|
fun `test invert function with dict causes error`() {
|
||||||
enterCommand("echo invert({1: 2, 3: 4})")
|
enterCommand("echo invert({1: 2, 3: 4})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
||||||
}
|
}
|
||||||
@@ -44,7 +42,6 @@ class InvertFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test invert function with float causes error`() {
|
fun `test invert function with float causes error`() {
|
||||||
enterCommand("echo invert(1.5)")
|
enterCommand("echo invert(1.5)")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E805: Using a Float as a Number")
|
assertPluginErrorMessage("E805: Using a Float as a Number")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -43,7 +43,6 @@ class OrFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test or function with list causes error`() {
|
fun `test or function with list causes error`() {
|
||||||
enterCommand("echo or([1, 2, 3], [2, 3, 4])")
|
enterCommand("echo or([1, 2, 3], [2, 3, 4])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E745: Using a List as a Number")
|
assertPluginErrorMessage("E745: Using a List as a Number")
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,6 @@ class OrFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test or function with dict causes error`() {
|
fun `test or function with dict causes error`() {
|
||||||
enterCommand("echo or({1: 2, 3: 4}, {3: 4, 5: 6})")
|
enterCommand("echo or({1: 2, 3: 4}, {3: 4, 5: 6})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
||||||
}
|
}
|
||||||
@@ -59,7 +57,6 @@ class OrFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test or function with float causes error`() {
|
fun `test or function with float causes error`() {
|
||||||
enterCommand("echo or(1.5, 2.5)")
|
enterCommand("echo or(1.5, 2.5)")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E805: Using a Float as a Number")
|
assertPluginErrorMessage("E805: Using a Float as a Number")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -43,7 +43,6 @@ class XorFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test xor function with list causes error`() {
|
fun `test xor function with list causes error`() {
|
||||||
enterCommand("echo xor([1, 2, 3], [2, 3, 4])")
|
enterCommand("echo xor([1, 2, 3], [2, 3, 4])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E745: Using a List as a Number")
|
assertPluginErrorMessage("E745: Using a List as a Number")
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,6 @@ class XorFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test xor function with dict causes error`() {
|
fun `test xor function with dict causes error`() {
|
||||||
enterCommand("echo xor({1: 2, 3: 4}, {3: 4, 5: 6})")
|
enterCommand("echo xor({1: 2, 3: 4}, {3: 4, 5: 6})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
assertPluginErrorMessage("E728: Using a Dictionary as a Number")
|
||||||
}
|
}
|
||||||
@@ -59,7 +57,6 @@ class XorFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test xor function with float causes error`() {
|
fun `test xor function with float causes error`() {
|
||||||
enterCommand("echo xor(1.5, 2.5)")
|
enterCommand("echo xor(1.5, 2.5)")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E805: Using a Float as a Number")
|
assertPluginErrorMessage("E805: Using a Float as a Number")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -33,7 +33,6 @@ class ToLowerFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test tolower with list causes error`() {
|
fun `test tolower with list causes error`() {
|
||||||
enterCommand("echo tolower([1, 2, 3])")
|
enterCommand("echo tolower([1, 2, 3])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E730: Using a List as a String")
|
assertPluginErrorMessage("E730: Using a List as a String")
|
||||||
}
|
}
|
||||||
@@ -41,7 +40,6 @@ class ToLowerFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test tolower with dict causes error`() {
|
fun `test tolower with dict causes error`() {
|
||||||
enterCommand("echo tolower({1: 2, 3: 4})")
|
enterCommand("echo tolower({1: 2, 3: 4})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E731: Using a Dictionary as a String")
|
assertPluginErrorMessage("E731: Using a Dictionary as a String")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2025 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -33,7 +33,6 @@ class ToUpperFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test toupper with list causes error`() {
|
fun `test toupper with list causes error`() {
|
||||||
enterCommand("echo toupper([1, 2, 3])")
|
enterCommand("echo toupper([1, 2, 3])")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E730: Using a List as a String")
|
assertPluginErrorMessage("E730: Using a List as a String")
|
||||||
}
|
}
|
||||||
@@ -41,7 +40,6 @@ class ToUpperFunctionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun `test toupper with dict causes error`() {
|
fun `test toupper with dict causes error`() {
|
||||||
enterCommand("echo toupper({1: 2, 3: 4})")
|
enterCommand("echo toupper({1: 2, 3: 4})")
|
||||||
assertNoExOutput()
|
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E731: Using a Dictionary as a String")
|
assertPluginErrorMessage("E731: Using a Dictionary as a String")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -207,7 +207,12 @@ class FunctionDeclarationTest : VimTestCase() {
|
|||||||
typeText(commandToKeys("echo F1()"))
|
typeText(commandToKeys("echo F1()"))
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E121: Undefined variable: x")
|
assertPluginErrorMessage("E121: Undefined variable: x")
|
||||||
assertExOutput("0")
|
assertExOutput(
|
||||||
|
"""
|
||||||
|
E121: Undefined variable: x
|
||||||
|
0
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
typeText(commandToKeys("delf! F1"))
|
typeText(commandToKeys("delf! F1"))
|
||||||
typeText(commandToKeys("delf! F2"))
|
typeText(commandToKeys("delf! F2"))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -154,7 +154,12 @@ class TryCatchTest : VimTestCase() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
assertPluginError(true)
|
assertPluginError(true)
|
||||||
assertExOutput("finally block")
|
assertExOutput(
|
||||||
|
"""
|
||||||
|
finally block
|
||||||
|
my exception
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -223,7 +223,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
) {
|
) {
|
||||||
enterCommand("set nowrapscan")
|
enterCommand("set nowrapscan")
|
||||||
}
|
}
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E385: Search hit BOTTOM without match for: one")
|
assertPluginErrorMessage("E385: Search hit BOTTOM without match for: one")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
three
|
three
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E486: Pattern not found: banana")
|
assertPluginErrorMessage("E486: Pattern not found: banana")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
) {
|
) {
|
||||||
enterCommand("set nowrapscan")
|
enterCommand("set nowrapscan")
|
||||||
}
|
}
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E384: Search hit TOP without match for: three")
|
assertPluginErrorMessage("E384: Search hit TOP without match for: three")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
three
|
three
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E486: Pattern not found: banana")
|
assertPluginErrorMessage("E486: Pattern not found: banana")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +615,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
enterCommand("set nowrapscan")
|
enterCommand("set nowrapscan")
|
||||||
typeText("10", "/", searchCommand("one"))
|
typeText("10", "/", searchCommand("one"))
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E385: Search hit BOTTOM without match for: one")
|
assertPluginErrorMessage("E385: Search hit BOTTOM without match for: one")
|
||||||
assertPosition(2, 0)
|
assertPosition(2, 0)
|
||||||
}
|
}
|
||||||
@@ -679,7 +679,7 @@ class SearchGroupTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
enterCommand("set nowrapscan")
|
enterCommand("set nowrapscan")
|
||||||
typeText("12", "?one<CR>")
|
typeText("12", "?one<CR>")
|
||||||
assertPluginError(false)
|
assertPluginError(true)
|
||||||
assertPluginErrorMessage("E384: Search hit TOP without match for: one")
|
assertPluginErrorMessage("E384: Search hit TOP without match for: one")
|
||||||
assertPosition(8, 0)
|
assertPosition(8, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ class JumpNavigationSplitTest : IdeaVimStarterTestBase() {
|
|||||||
openFile(longFile("Jump1"))
|
openFile(longFile("Jump1"))
|
||||||
|
|
||||||
typeVim("G")
|
typeVim("G")
|
||||||
pause(500)
|
pause(300)
|
||||||
assertCaretAfter(40, "G should go to end of file")
|
assertCaretAfter(40, "G should go to end of file")
|
||||||
|
|
||||||
ctrlO()
|
ctrlO()
|
||||||
pause(500)
|
pause(300)
|
||||||
assertCaretBefore(10, "Ctrl-O should jump back to start")
|
assertCaretBefore(10, "Ctrl-O should jump back to start")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class JumpNavigationSplitTest : IdeaVimStarterTestBase() {
|
|||||||
openFile(longFile("Jump2"))
|
openFile(longFile("Jump2"))
|
||||||
|
|
||||||
typeVim("gg")
|
typeVim("gg")
|
||||||
pause(500)
|
pause(300)
|
||||||
|
|
||||||
typeVim("/Line 30\n")
|
typeVim("/Line 30\n")
|
||||||
pause()
|
pause()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2026 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -13,22 +13,14 @@ import com.intellij.vim.annotations.Mode
|
|||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
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.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.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.isCommandLineActionChar
|
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState
|
import com.maddyhome.idea.vim.state.KeyHandlerState
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
/**
|
@CommandOrMotion(keys = ["<C-K>"], modes = [Mode.INSERT, Mode.CMD_LINE])
|
||||||
* Insert mode: insert a digraph character via `<C-K>`
|
|
||||||
*
|
|
||||||
* The converted digraph character is re-injected through the key handler so that it is processed as typed input in
|
|
||||||
* Insert mode (handled by the change group).
|
|
||||||
*/
|
|
||||||
@CommandOrMotion(keys = ["<C-K>"], modes = [Mode.INSERT])
|
|
||||||
class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
override val type: Command.Type = Command.Type.INSERT
|
||||||
|
|
||||||
@@ -37,6 +29,22 @@ class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
|||||||
// We're waiting for it to complete and give us a CHARACTER
|
// We're waiting for it to complete and give us a CHARACTER
|
||||||
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform additional initialisation when starting to wait for an argument
|
||||||
|
*
|
||||||
|
* IdeaVim has two ways of handling digraphs/literals. Actions such as `r` or `f` can accept a digraph, which really
|
||||||
|
* means it accepts a character, but the user can use `<C-K>`/`<C-V>` to type a digraph or literal and convert it into
|
||||||
|
* a character. Unfortunately, there is no mode that can be used to register an "insert digraph/literal" action for
|
||||||
|
* these keys while replace or find is active. So the key handler hard codes these keys and will check for them when
|
||||||
|
* an action expects a digraph (and like Vim, these keys cannot be mapped). Once the state machine has matched a
|
||||||
|
* character, the expected argument is reset to [Argument.Type.CHARACTER] and the character is passed through the key
|
||||||
|
* handler again, potentially mapped, and then attached as an argument to the current command, which is now complete
|
||||||
|
* and executed.
|
||||||
|
*
|
||||||
|
* In Insert and Command-line mode, the `<C-K>` and `<C-V>` keys are actions that will wait for a character argument,
|
||||||
|
* and then insert it. Commands are only executed once complete, so we use [onStartWaitingForArgument] to start the
|
||||||
|
* digraph state machine. This also gives us a repeatable command and captures the keys for `'showcmd'`.
|
||||||
|
*/
|
||||||
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
||||||
val result = keyState.digraphSequence.startDigraphSequence()
|
val result = keyState.digraphSequence.startDigraphSequence()
|
||||||
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
||||||
@@ -48,6 +56,7 @@ class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
|||||||
cmd: Command,
|
cmd: Command,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
// The converted digraph character has been captured as an argument, push it back through key handler
|
||||||
val argument = cmd.argument as? Argument.Character ?: return false
|
val argument = cmd.argument as? Argument.Character ?: return false
|
||||||
val keyStroke = KeyStroke.getKeyStroke(argument.character)
|
val keyStroke = KeyStroke.getKeyStroke(argument.character)
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
@@ -55,37 +64,3 @@ class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Command-line mode: insert a digraph character via `<C-K>`
|
|
||||||
*
|
|
||||||
* Control characters like Escape or Enter are inserted directly into the command line to avoid being matched as
|
|
||||||
* commands. Other characters use [VimCommandLine.handleKey] so that overwrite mode is handled correctly.
|
|
||||||
*/
|
|
||||||
@CommandOrMotion(keys = ["<C-K>"], modes = [Mode.CMD_LINE])
|
|
||||||
class CmdLineCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
|
||||||
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
|
||||||
|
|
||||||
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
|
||||||
val result = keyState.digraphSequence.startDigraphSequence()
|
|
||||||
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
cmd: Command,
|
|
||||||
operatorArguments: OperatorArguments,
|
|
||||||
): Boolean {
|
|
||||||
val argument = cmd.argument as? Argument.Character ?: return false
|
|
||||||
val commandLine = injector.commandLine.getActiveCommandLine() ?: return false
|
|
||||||
val ch = argument.character
|
|
||||||
if (ch.isCommandLineActionChar()) {
|
|
||||||
commandLine.insertText(commandLine.caret.offset, ch.toString())
|
|
||||||
} else {
|
|
||||||
commandLine.handleKey(KeyStroke.getKeyStroke(ch))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2026 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -13,22 +13,14 @@ import com.intellij.vim.annotations.Mode
|
|||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
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.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.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.isCommandLineActionChar
|
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState
|
import com.maddyhome.idea.vim.state.KeyHandlerState
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
/**
|
@CommandOrMotion(keys = ["<C-V>", "<C-Q>"], modes = [Mode.INSERT, Mode.CMD_LINE])
|
||||||
* Insert mode: insert a literal character via `<C-V>` / `<C-Q>`
|
|
||||||
*
|
|
||||||
* The converted literal character is re-injected through the key handler so that it is processed as typed input in
|
|
||||||
* Insert mode (handled by the change group).
|
|
||||||
*/
|
|
||||||
@CommandOrMotion(keys = ["<C-V>", "<C-Q>"], modes = [Mode.INSERT])
|
|
||||||
class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
override val type: Command.Type = Command.Type.INSERT
|
||||||
|
|
||||||
@@ -37,6 +29,22 @@ class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
|||||||
// We're waiting for it to complete and give us a CHARACTER
|
// We're waiting for it to complete and give us a CHARACTER
|
||||||
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform additional initialisation when starting to wait for an argument
|
||||||
|
*
|
||||||
|
* IdeaVim has two ways of handling digraphs/literals. Actions such as `r` or `f` can accept a digraph, which really
|
||||||
|
* means it accepts a character, but the user can use `<C-K>`/`<C-V>` to type a digraph or literal and convert it into
|
||||||
|
* a character. Unfortunately, there is no mode that can be used to register an "insert digraph/literal" action for
|
||||||
|
* these keys while replace or find is active. So the key handler hard codes these keys and will check for them when
|
||||||
|
* an action expects a digraph (and like Vim, these keys cannot be mapped). Once the state machine has matched a
|
||||||
|
* character, the expected argument is reset to [Argument.Type.CHARACTER] and the character is passed through the key
|
||||||
|
* handler again, potentially mapped, and then attached as an argument to the current command, which is now complete
|
||||||
|
* and executed.
|
||||||
|
*
|
||||||
|
* In Insert and Command-line mode, the `<C-K>` and `<C-V>` keys are actions that will wait for a character argument,
|
||||||
|
* and then insert it. Commands are only executed once complete, so we use [onStartWaitingForArgument] to start the
|
||||||
|
* digraph state machine. This also gives us a repeatable command and captures the keys for `'showcmd'`.
|
||||||
|
*/
|
||||||
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
||||||
val result = keyState.digraphSequence.startLiteralSequence()
|
val result = keyState.digraphSequence.startLiteralSequence()
|
||||||
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
||||||
@@ -48,6 +56,7 @@ class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
|||||||
cmd: Command,
|
cmd: Command,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
// The converted literal character has been captured as an argument, push it back through key handler
|
||||||
val argument = cmd.argument as? Argument.Character ?: return false
|
val argument = cmd.argument as? Argument.Character ?: return false
|
||||||
val keyStroke = KeyStroke.getKeyStroke(argument.character)
|
val keyStroke = KeyStroke.getKeyStroke(argument.character)
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
@@ -55,39 +64,3 @@ class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Command-line mode: insert a literal character via `<C-V>` / `<C-Q>`
|
|
||||||
*
|
|
||||||
* Control characters like Escape or Enter are inserted directly into the command line to avoid being matched as
|
|
||||||
* commands (e.g., LeaveCommandLineAction). Other characters use [VimCommandLine.handleKey] so that overwrite mode
|
|
||||||
* is handled correctly.
|
|
||||||
*/
|
|
||||||
@CommandOrMotion(keys = ["<C-V>", "<C-Q>"], modes = [Mode.CMD_LINE])
|
|
||||||
class CmdLineCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
|
||||||
override val argumentType: Argument.Type = Argument.Type.DIGRAPH
|
|
||||||
|
|
||||||
override fun onStartWaitingForArgument(editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
|
||||||
val result = keyState.digraphSequence.startLiteralSequence()
|
|
||||||
KeyHandler.getInstance().setPromptCharacterEx(result.promptCharacter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
cmd: Command,
|
|
||||||
operatorArguments: OperatorArguments,
|
|
||||||
): Boolean {
|
|
||||||
val argument = cmd.argument as? Argument.Character ?: return false
|
|
||||||
val commandLine = injector.commandLine.getActiveCommandLine() ?: return false
|
|
||||||
val ch = argument.character
|
|
||||||
if (ch.isCommandLineActionChar()) {
|
|
||||||
// Insert directly to avoid these being matched as commands by the key handler
|
|
||||||
commandLine.insertText(commandLine.caret.offset, ch.toString())
|
|
||||||
} else {
|
|
||||||
commandLine.handleKey(KeyStroke.getKeyStroke(ch))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -12,7 +12,25 @@ import com.maddyhome.idea.vim.helper.EngineMessageHelper
|
|||||||
import org.jetbrains.annotations.PropertyKey
|
import org.jetbrains.annotations.PropertyKey
|
||||||
|
|
||||||
interface VimMessages {
|
interface VimMessages {
|
||||||
|
/**
|
||||||
|
* Displays an informational message to the user.
|
||||||
|
* The message panel closes on any keystroke and passes the key through to the editor.
|
||||||
|
*/
|
||||||
|
fun showMessage(editor: VimEditor, message: String?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an error message to the user (typically in red).
|
||||||
|
* The message panel closes on any keystroke and passes the key through to the editor.
|
||||||
|
*/
|
||||||
|
fun showErrorMessage(editor: VimEditor, message: String?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy method for displaying messages.
|
||||||
|
* @deprecated Use [showMessage] or [showErrorMessage] instead.
|
||||||
|
*/
|
||||||
|
@Deprecated("Use showMessage or showErrorMessage instead", ReplaceWith("showMessage(editor, message)"))
|
||||||
fun showStatusBarMessage(editor: VimEditor?, message: String?)
|
fun showStatusBarMessage(editor: VimEditor?, message: String?)
|
||||||
|
|
||||||
fun getStatusBarMessage(): String?
|
fun getStatusBarMessage(): String?
|
||||||
fun clearStatusBarMessage()
|
fun clearStatusBarMessage()
|
||||||
fun indicateError()
|
fun indicateError()
|
||||||
@@ -28,13 +46,4 @@ interface VimMessages {
|
|||||||
fun message(@PropertyKey(resourceBundle = EngineMessageHelper.BUNDLE) key: String, vararg params: Any): String
|
fun message(@PropertyKey(resourceBundle = EngineMessageHelper.BUNDLE) key: String, vararg params: Any): String
|
||||||
|
|
||||||
fun updateStatusBar(editor: VimEditor)
|
fun updateStatusBar(editor: VimEditor)
|
||||||
|
|
||||||
fun showMessage(editor: VimEditor, message: String) {
|
|
||||||
showStatusBarMessage(editor, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showErrorMessage(editor: VimEditor, message: String?) {
|
|
||||||
showStatusBarMessage(editor, message)
|
|
||||||
indicateError()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -29,7 +29,8 @@ interface VimOutputPanel {
|
|||||||
* Note: The full text content is not updated in the display until [show] is invoked.
|
* Note: The full text content is not updated in the display until [show] is invoked.
|
||||||
*
|
*
|
||||||
* @param text The text to append.
|
* @param text The text to append.
|
||||||
* @param isNewLine Whether to start the appended text on a new line. Defaults to true.
|
* @param isNewLine Whether to start the appended text on a new line.
|
||||||
|
* @param messageType The type of message, used to determine text styling.
|
||||||
*/
|
*/
|
||||||
fun addText(text: String, isNewLine: Boolean = true, messageType: MessageType = MessageType.STANDARD)
|
fun addText(text: String, isNewLine: Boolean = true, messageType: MessageType = MessageType.STANDARD)
|
||||||
|
|
||||||
@@ -51,4 +52,4 @@ interface VimOutputPanel {
|
|||||||
|
|
||||||
fun setContent(text: String)
|
fun setContent(text: String)
|
||||||
fun clearText()
|
fun clearText()
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -26,8 +26,12 @@ interface VimOutputPanelService {
|
|||||||
fun getCurrentOutputPanel(): VimOutputPanel?
|
fun getCurrentOutputPanel(): VimOutputPanel?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends text to the existing output panel or creates a new one with the given text.
|
* Appends text to the existing output panel or creates a new one with the given text and message type.
|
||||||
* Basic method that should be sufficient in most cases.
|
|
||||||
*/
|
*/
|
||||||
fun output(editor: VimEditor, context: ExecutionContext, text: String)
|
fun output(
|
||||||
}
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
|
text: String,
|
||||||
|
messageType: MessageType = MessageType.STANDARD,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
* Copyright 2003-2026 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -13,9 +13,9 @@ abstract class VimOutputPanelServiceBase : VimOutputPanelService {
|
|||||||
return getCurrentOutputPanel() ?: create(editor, context)
|
return getCurrentOutputPanel() ?: create(editor, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun output(editor: VimEditor, context: ExecutionContext, text: String) {
|
override fun output(editor: VimEditor, context: ExecutionContext, text: String, messageType: MessageType) {
|
||||||
val panel = getOrCreate(editor, context)
|
val panel = getOrCreate(editor, context)
|
||||||
panel.addText(text)
|
panel.addText(text, true, messageType)
|
||||||
panel.show()
|
panel.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,11 +160,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
return TextRange(start, end)
|
return TextRange(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findWordAtOrFollowingCursor(
|
override fun findWordAtOrFollowingCursor(editor: VimEditor, caret: ImmutableVimCaret, isBigWord: Boolean): TextRange? {
|
||||||
editor: VimEditor,
|
|
||||||
caret: ImmutableVimCaret,
|
|
||||||
isBigWord: Boolean,
|
|
||||||
): TextRange? {
|
|
||||||
val offset = caret.offset
|
val offset = caret.offset
|
||||||
return findWordAtOrFollowingCursor(editor, offset, isBigWord)
|
return findWordAtOrFollowingCursor(editor, offset, isBigWord)
|
||||||
}
|
}
|
||||||
@@ -172,7 +168,6 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
override fun findFilenameAtOrFollowingCursor(editor: VimEditor, caret: ImmutableVimCaret): TextRange? {
|
override fun findFilenameAtOrFollowingCursor(editor: VimEditor, caret: ImmutableVimCaret): TextRange? {
|
||||||
return findFilenameAtOrFollowingCursor(editor, caret.offset)
|
return findFilenameAtOrFollowingCursor(editor, caret.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findFilenameAtOrFollowingCursor(editor: VimEditor, offset: Int): TextRange? {
|
override fun findFilenameAtOrFollowingCursor(editor: VimEditor, offset: Int): TextRange? {
|
||||||
val text = editor.text()
|
val text = editor.text()
|
||||||
if (text.isEmpty()) return null
|
if (text.isEmpty()) return null
|
||||||
@@ -305,9 +300,6 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result is VimMatchResult.Failure) {
|
if (result is VimMatchResult.Failure) {
|
||||||
if (!showMessages) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (wrap) {
|
if (wrap) {
|
||||||
injector.messages.showErrorMessage(editor, injector.messages.message("E486", pattern))
|
injector.messages.showErrorMessage(editor, injector.messages.message("E486", pattern))
|
||||||
} else if (dir === Direction.FORWARDS) {
|
} else if (dir === Direction.FORWARDS) {
|
||||||
@@ -315,6 +307,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
} else {
|
} else {
|
||||||
injector.messages.showErrorMessage(editor, injector.messages.message("E384", pattern))
|
injector.messages.showErrorMessage(editor, injector.messages.message("E384", pattern))
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// When trying to find the end position for a match, we're allowed to match the current position. But if we do that
|
// When trying to find the end position for a match, we're allowed to match the current position. But if we do that
|
||||||
@@ -341,10 +334,6 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result is VimMatchResult.Failure) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result as VimMatchResult.Success).range
|
return (result as VimMatchResult.Success).range
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1779,8 +1768,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
|
|
||||||
if (isOuter && shouldEndOnWhitespace && start > 0
|
if (isOuter && shouldEndOnWhitespace && start > 0
|
||||||
&& !isWhitespace(editor, chars[end], isBig)
|
&& !isWhitespace(editor, chars[end], isBig)
|
||||||
&& !isWhitespace(editor, chars[start], isBig)
|
&& !isWhitespace(editor, chars[start], isBig)) {
|
||||||
) {
|
|
||||||
|
|
||||||
// Outer word objects normally include following whitespace. But if there's no following whitespace to include,
|
// Outer word objects normally include following whitespace. But if there's no following whitespace to include,
|
||||||
// we should extend the range to include preceding whitespace. However, Vim doesn't select whitespace at the
|
// we should extend the range to include preceding whitespace. However, Vim doesn't select whitespace at the
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2026 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
@@ -18,13 +18,3 @@ fun KeyStroke.isCloseKeyStroke(): Boolean {
|
|||||||
keyCode == KeyEvent.VK_C && modifiers and InputEvent.CTRL_DOWN_MASK != 0 ||
|
keyCode == KeyEvent.VK_C && modifiers and InputEvent.CTRL_DOWN_MASK != 0 ||
|
||||||
keyCode == '['.code && modifiers and InputEvent.CTRL_DOWN_MASK != 0
|
keyCode == '['.code && modifiers and InputEvent.CTRL_DOWN_MASK != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this character would be matched as a command-line action (close or execute) rather than text input
|
|
||||||
* when re-injected through the key handler in CMD_LINE mode.
|
|
||||||
*
|
|
||||||
* Escape closes the command line, Enter/CR executes it.
|
|
||||||
*/
|
|
||||||
fun Char.isCommandLineActionChar(): Boolean {
|
|
||||||
return this == '\u001B' || this == '\n' || this == '\r'
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ sealed class Command(
|
|||||||
if (Flag.SAVE_SELECTION !in argFlags.flags) {
|
if (Flag.SAVE_SELECTION !in argFlags.flags) {
|
||||||
// Editor.inBlockSelection is not available, because we're not in Visual mode anymore. Check if the primary caret
|
// Editor.inBlockSelection is not available, because we're not in Visual mode anymore. Check if the primary caret
|
||||||
// currently has a selection and if (when we still in Visual) it was a block selection.
|
// currently has a selection and if (when we still in Visual) it was a block selection.
|
||||||
injector.application.runWriteAction {
|
injector.application.runReadAction {
|
||||||
if (editor.primaryCaret().hasSelection() && editor.primaryCaret().lastSelectionInfo.selectionType.isBlock) {
|
if (editor.primaryCaret().hasSelection() && editor.primaryCaret().lastSelectionInfo.selectionType.isBlock) {
|
||||||
editor.removeSecondaryCarets()
|
editor.removeSecondaryCarets()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,15 +299,10 @@
|
|||||||
"class": "com.maddyhome.idea.vim.action.motion.updown.MotionDownAction",
|
"class": "com.maddyhome.idea.vim.action.motion.updown.MotionDownAction",
|
||||||
"modes": "NXO"
|
"modes": "NXO"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"keys": "<C-K>",
|
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.CmdLineCompletedDigraphAction",
|
|
||||||
"modes": "C"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"keys": "<C-K>",
|
"keys": "<C-K>",
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction",
|
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction",
|
||||||
"modes": "I"
|
"modes": "IC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keys": "<C-Left>",
|
"keys": "<C-Left>",
|
||||||
@@ -414,15 +409,10 @@
|
|||||||
"class": "com.maddyhome.idea.vim.action.window.tabs.PreviousTabAction",
|
"class": "com.maddyhome.idea.vim.action.window.tabs.PreviousTabAction",
|
||||||
"modes": "NXO"
|
"modes": "NXO"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"keys": "<C-Q>",
|
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.CmdLineCompletedLiteralAction",
|
|
||||||
"modes": "C"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"keys": "<C-Q>",
|
"keys": "<C-Q>",
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction",
|
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction",
|
||||||
"modes": "I"
|
"modes": "IC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keys": "<C-R>",
|
"keys": "<C-R>",
|
||||||
@@ -569,15 +559,10 @@
|
|||||||
"class": "com.maddyhome.idea.vim.action.motion.scroll.CtrlUpAction",
|
"class": "com.maddyhome.idea.vim.action.motion.scroll.CtrlUpAction",
|
||||||
"modes": "N"
|
"modes": "N"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"keys": "<C-V>",
|
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.CmdLineCompletedLiteralAction",
|
|
||||||
"modes": "C"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"keys": "<C-V>",
|
"keys": "<C-V>",
|
||||||
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction",
|
"class": "com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction",
|
||||||
"modes": "I"
|
"modes": "IC"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keys": "<C-W>",
|
"keys": "<C-W>",
|
||||||
|
|||||||
Reference in New Issue
Block a user