mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-25 07:42:59 +01:00
Compare commits
189 Commits
a7def05aa8
...
87b45da4ee
Author | SHA1 | Date | |
---|---|---|---|
87b45da4ee | |||
56ee5f3bd4 | |||
aef6016b33 | |||
c2c23d8059 | |||
854563130a | |||
7c61822106 | |||
4fce924424 | |||
56e8da8808 | |||
c5d108c3d6 | |||
c76e4eabd4 | |||
98c9579d8d | |||
f060750768 | |||
83c2e1c362 | |||
6555e8dddd | |||
528fd1b1d3 | |||
932fb509ff | |||
f353f47987 | |||
5410187bd5 | |||
|
58a8b96c3c | ||
|
0e057ca9ae | ||
|
36bf2639bb | ||
|
0c1326e689 | ||
|
dd74438f68 | ||
|
a9ddfac782 | ||
|
79437df894 | ||
|
b5a04af089 | ||
|
52372ae3d3 | ||
|
65d755d9b2 | ||
|
1f1a8f3395 | ||
|
629e4e7053 | ||
|
c50a299cfd | ||
|
4bad129caf | ||
|
1ffb28e21b | ||
|
c126243367 | ||
|
6da6e461a8 | ||
|
103101bbcb | ||
|
f737fcba1a | ||
|
c5fa0678b8 | ||
|
00ccddf8cf | ||
|
00cbf188fb | ||
|
988ea74461 | ||
|
0914cda7e5 | ||
|
5959e9aaa1 | ||
|
434df565ae | ||
|
c8f36504d8 | ||
|
06e1af371e | ||
|
d744987ac8 | ||
|
b4eef17aaa | ||
|
5c50e8607c | ||
|
9a324ab448 | ||
|
c3978335f5 | ||
|
051296c2aa | ||
|
90f2d2ff29 | ||
|
4c2edab406 | ||
|
76e8fd69bf | ||
|
5dd458bcf7 | ||
|
a94a8b8539 | ||
|
261230b23a | ||
|
b90317e00e | ||
|
21c9dc8785 | ||
|
31bbc60325 | ||
|
fec6e5c189 | ||
|
23c1493f17 | ||
|
00808af569 | ||
|
3c94091d30 | ||
|
b737362aba | ||
|
db722fc4e5 | ||
|
7d679e68dc | ||
|
bc808403fb | ||
|
9d6dc317a4 | ||
|
cf29c50f31 | ||
|
2a3c4cc441 | ||
|
bd192561ae | ||
|
66ff56a05e | ||
|
def86d179e | ||
|
3c9a343f8b | ||
|
10b6b05fab | ||
|
caa4ef736a | ||
|
23702345a9 | ||
|
ba89babd10 | ||
|
2ce3fbd677 | ||
|
d8de73a06d | ||
|
8094e6711a | ||
|
10edccc1d6 | ||
|
247aaed188 | ||
|
1a4333fa1b | ||
|
8eaa6df318 | ||
|
7523db186f | ||
|
4aac113522 | ||
|
795abd77a7 | ||
|
38bc914504 | ||
|
c8113eea83 | ||
|
924b7418e8 | ||
|
a7dfef61e9 | ||
|
db35c979b4 | ||
|
2de933c723 | ||
|
d3704d602f | ||
|
ea62f227bf | ||
|
23fdadc32e | ||
|
e9bf06686f | ||
|
7842b155c1 | ||
|
74a8277e10 | ||
|
ddb1b80463 | ||
|
eea3336934 | ||
|
f801145712 | ||
|
e033b08535 | ||
|
1d9514a205 | ||
|
6741120f19 | ||
|
c501457322 | ||
|
46425a24c3 | ||
|
9826f0a7f0 | ||
|
43175061e0 | ||
|
0ab32cac34 | ||
|
e3ec9c614b | ||
|
f454d60234 | ||
|
19fa00837c | ||
|
275c5d28e1 | ||
|
15ae069f6f | ||
|
00f5541dc6 | ||
|
02540eb303 | ||
|
282e581bdb | ||
|
31e7c49608 | ||
|
7966a6dc91 | ||
|
5fc2f04224 | ||
|
6edfd8ed22 | ||
|
363db05db7 | ||
|
3738012dd6 | ||
|
355cfe035d | ||
|
6d01b5be77 | ||
|
4938957483 | ||
|
46f4fa7cdd | ||
|
f696135f31 | ||
|
52e0fcdc7d | ||
|
ac17518a23 | ||
6dd924b2b2 | |||
|
f439474b73 | ||
|
d6cd92e256 | ||
|
3a294268d9 | ||
|
9b81c7e650 | ||
|
e229fb3ad7 | ||
|
720eae63fa | ||
|
0df96a24bd | ||
|
21a1588ede | ||
|
7970006e8c | ||
|
418d0cff7f | ||
|
7284360774 | ||
|
9fc3fadee8 | ||
|
3d2db56f63 | ||
|
e9c7cb8670 | ||
|
87d19274c5 | ||
|
3161bf8ffd | ||
|
b68865587e | ||
|
7dc0dbe944 | ||
|
f50a363525 | ||
|
57ad4c70d1 | ||
|
d3d93b898f | ||
|
7d8973edb2 | ||
|
2302b576b0 | ||
f4782630d4 | |||
|
8c1a2a686f | ||
32d5e1e6fa | |||
|
a381a1cacc | ||
|
73c3c9f7fe | ||
|
67ef0a75d5 | ||
|
328bc5e95a | ||
|
7f8021e37e | ||
|
9701b7e79b | ||
|
7a52c6fec9 | ||
|
1503639d4b | ||
|
e82f19c852 | ||
|
edd69c9c25 | ||
|
fc61e369fb | ||
|
113586b59b | ||
|
5dbd5e1c89 | ||
|
04b7d9e2c3 | ||
|
5f2743176a | ||
|
3723488617 | ||
|
0cc17a0791 | ||
|
05a21e6091 | ||
|
fc06bc7c6f | ||
|
1bd005adc1 | ||
|
4f208d1577 | ||
|
eb6e0557a7 | ||
|
cf09d66be6 | ||
|
76cd127a8a | ||
|
f6dd2a9968 | ||
|
ae05a33e14 | ||
|
b38fad323b | ||
|
c6027fcf0f |
2
.github/workflows/mergePr.yml
vendored
2
.github/workflows/mergePr.yml
vendored
@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
if: github.event.pull_request.merged == true && github.repository == 'JetBrains/ideavim'
|
||||
if: false
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
6
.github/workflows/runUiOctopusTests.yml
vendored
6
.github/workflows/runUiOctopusTests.yml
vendored
@ -9,9 +9,6 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Apply Patch
|
||||
run: |
|
||||
git apply tests/ui-ij-tests/src/test/kotlin/ui/octopus.patch
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
@ -30,7 +27,7 @@ jobs:
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle runIdeForUiTests > build/reports/idea.log &
|
||||
gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@ -52,6 +49,7 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
|
1
.github/workflows/runUiPyTests.yml
vendored
1
.github/workflows/runUiPyTests.yml
vendored
@ -52,4 +52,5 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-py-tests/build/reports
|
||||
sandbox-idea-log
|
1
.github/workflows/runUiTests.yml
vendored
1
.github/workflows/runUiTests.yml
vendored
@ -49,6 +49,7 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
|
5
.github/workflows/updateChangelog.yml
vendored
5
.github/workflows/updateChangelog.yml
vendored
@ -7,15 +7,12 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
# Workflow run on push is disabled to avoid conflicts when merging PR
|
||||
# push:
|
||||
# branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
if: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
3
.teamcity/_Self/buildTypes/Qodana.kt
vendored
3
.teamcity/_Self/buildTypes/Qodana.kt
vendored
@ -46,8 +46,8 @@ object Qodana : IdeaVimBuildType({
|
||||
version = Qodana.JVMVersion.LATEST
|
||||
}
|
||||
reportAsTests = true
|
||||
additionalDockerArguments = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs"
|
||||
additionalQodanaArguments = "--baseline qodana.sarif.json"
|
||||
cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,6 @@ object Qodana : IdeaVimBuildType({
|
||||
timezone = "SERVER"
|
||||
}
|
||||
param("dayOfWeek", "Sunday")
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
55
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
55
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@ -97,14 +97,14 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Set TeamCity build number"
|
||||
tasks = "scripts:setTeamCityBuildNumber"
|
||||
}
|
||||
gradle {
|
||||
name = "Update change log"
|
||||
tasks = "scripts:changelogUpdateUnreleased"
|
||||
}
|
||||
gradle {
|
||||
name = "Commit preparation changes"
|
||||
tasks = "scripts:commitChanges"
|
||||
}
|
||||
// gradle {
|
||||
// name = "Update change log"
|
||||
// tasks = "scripts:changelogUpdateUnreleased"
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Commit preparation changes"
|
||||
// tasks = "scripts:commitChanges"
|
||||
// }
|
||||
gradle {
|
||||
name = "Add release tag"
|
||||
tasks = "scripts:addReleaseTag"
|
||||
@ -117,33 +117,24 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Publish release"
|
||||
tasks = "publishPlugin"
|
||||
}
|
||||
script {
|
||||
name = "Checkout master branch"
|
||||
scriptContent = """
|
||||
echo Checkout master
|
||||
git checkout master
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
name = "Update change log in master"
|
||||
tasks = "scripts:changelogUpdateUnreleased"
|
||||
}
|
||||
gradle {
|
||||
name = "Commit preparation changes in master"
|
||||
tasks = "scripts:commitChanges"
|
||||
}
|
||||
// script {
|
||||
// name = "Checkout master branch"
|
||||
// scriptContent = """
|
||||
// echo Checkout master
|
||||
// git checkout master
|
||||
// """.trimIndent()
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Update change log in master"
|
||||
// tasks = "scripts:changelogUpdateUnreleased"
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Commit preparation changes in master"
|
||||
// tasks = "scripts:commitChanges"
|
||||
// }
|
||||
script {
|
||||
name = "Push changes to the repo"
|
||||
scriptContent = """
|
||||
branch=$(git branch --show-current)
|
||||
echo Current branch is ${'$'}branch
|
||||
if [ "master" != "${'$'}branch" ];
|
||||
then
|
||||
git checkout master
|
||||
fi
|
||||
|
||||
git push origin
|
||||
|
||||
git checkout release
|
||||
echo checkout release branch
|
||||
git branch --set-upstream-to=origin/release release
|
||||
|
@ -1,11 +1,9 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
@ -13,6 +11,18 @@ To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
|
||||
check(artifactRules == """
|
||||
+:build/reports => build/reports
|
||||
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
||||
""".trimIndent()) {
|
||||
"Unexpected option value: artifactRules = $artifactRules"
|
||||
}
|
||||
artifactRules = """
|
||||
+:build/reports => build/reports
|
||||
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
||||
+:tests/java-tests/build/reports => tests/java-tests/build/reports
|
||||
""".trimIndent()
|
||||
|
||||
expectSteps {
|
||||
gradle {
|
||||
tasks = "clean test"
|
||||
|
@ -495,6 +495,14 @@ Contributors:
|
||||
[![icon][github]](https://github.com/emanuelgestosa)
|
||||
|
||||
Emanuel Gestosa
|
||||
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
|
||||
[![icon][github]](https://github.com/lippfi)
|
||||
|
||||
lippfi,
|
||||
* [![icon][mail]](mailto:fillipser143@gmail.com)
|
||||
[![icon][github]](https://github.com/Parker7123)
|
||||
|
||||
FilipParker
|
||||
|
||||
Previous contributors:
|
||||
|
||||
|
12
CHANGES.md
12
CHANGES.md
@ -23,15 +23,19 @@ It is important to distinguish EAP from traditional pre-release software.
|
||||
Please note that the quality of EAP versions may at times be way below even
|
||||
usual beta standards.
|
||||
|
||||
## To Be Released
|
||||
## End of changelog file maintenance
|
||||
|
||||
Since version 2.9.0, the changelog can be found on YouTrack
|
||||
|
||||
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
|
||||
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
|
||||
|
||||
## 2.9.0, 2024-02-20
|
||||
|
||||
### Fixes:
|
||||
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
|
||||
* [VIM-3291](https://youtrack.jetbrains.com/issue/VIM-3291) Remove sync of editor selection between different opened editors
|
||||
* [VIM-3234](https://youtrack.jetbrains.com/issue/VIM-3234) The space character won't mix in the tab chars after >> and << commands
|
||||
|
||||
### Merged PRs:
|
||||
* [725](https://github.com/JetBrains/ideavim/pull/725) by [Emanuel Gestosa](https://github.com/emanuelgestosa): Regex
|
||||
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
|
||||
|
||||
## 2.8.0, 2024-01-30
|
||||
|
@ -21,7 +21,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.19")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
||||
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||
|
@ -32,7 +32,6 @@ import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.RepositoryBuilder
|
||||
import org.intellij.markdown.ast.getTextInNode
|
||||
import org.jetbrains.changelog.Changelog
|
||||
import org.jetbrains.changelog.exceptions.MissingVersionException
|
||||
import org.kohsuke.github.GHUser
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
@ -49,14 +48,14 @@ buildscript {
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||
classpath("org.kohsuke:github-api:1.305")
|
||||
|
||||
classpath("io.ktor:ktor-client-core:2.3.7")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.7")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.7")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.7")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
|
||||
classpath("io.ktor:ktor-client-core:2.3.9")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.9")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.9")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.9")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
||||
|
||||
// This comes from the changelog plugin
|
||||
// classpath("org.jetbrains:markdown:0.3.1")
|
||||
@ -70,7 +69,7 @@ plugins {
|
||||
application
|
||||
id("java-test-fixtures")
|
||||
|
||||
id("org.jetbrains.intellij") version "1.17.1"
|
||||
id("org.jetbrains.intellij") version "1.17.2"
|
||||
id("org.jetbrains.changelog") version "2.2.0"
|
||||
|
||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||
@ -144,12 +143,12 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||
}
|
||||
|
||||
configurations {
|
||||
@ -207,6 +206,11 @@ tasks {
|
||||
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
|
||||
systemProperty("jb.consents.confirmation.enabled", "false")
|
||||
systemProperty("ide.show.tips.on.startup.default.value", "false")
|
||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||
}
|
||||
|
||||
runIde {
|
||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +264,9 @@ tasks {
|
||||
runPluginVerifier {
|
||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
||||
teamCityOutputFormat.set(true)
|
||||
// ideVersions.set(listOf("IC-2021.3.4"))
|
||||
|
||||
// The latest version of the plugin verifier is broken, so temporally use the stable version
|
||||
verifierVersion = "1.307"
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
@ -305,26 +311,14 @@ tasks {
|
||||
from(createOpenApiSourceJar) { into("lib/src") }
|
||||
}
|
||||
|
||||
val pluginVersion = version
|
||||
patchPluginXml {
|
||||
// Don't forget to update plugin.xml
|
||||
patchPluginXml {
|
||||
// Get the latest available change notes from the changelog file
|
||||
changeNotes.set(
|
||||
provider {
|
||||
with(changelog) {
|
||||
val log = try {
|
||||
getUnreleased()
|
||||
} catch (e: MissingVersionException) {
|
||||
getOrNull(pluginVersion.toString()) ?: getLatest()
|
||||
}
|
||||
renderItem(
|
||||
log,
|
||||
org.jetbrains.changelog.Changelog.OutputType.HTML,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
sinceBuild.set("233.11799.67")
|
||||
|
||||
changeNotes.set(
|
||||
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
@ -429,12 +423,14 @@ val prId: String by project
|
||||
|
||||
tasks.register("updateMergedPr") {
|
||||
doLast {
|
||||
if (project.hasProperty("prId")) {
|
||||
println("Got pr id: $prId")
|
||||
updateMergedPr(prId.toInt())
|
||||
} else {
|
||||
error("Cannot get prId")
|
||||
}
|
||||
val x = changelog.getUnreleased()
|
||||
println("x")
|
||||
// if (project.hasProperty("prId")) {
|
||||
// println("Got pr id: $prId")
|
||||
// updateMergedPr(prId.toInt())
|
||||
// } else {
|
||||
// error("Cannot get prId")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,13 @@
|
||||
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||
ideaVersion=2023.3.3
|
||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||
ideaType=IC
|
||||
downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=chylex-28
|
||||
version=chylex-29
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.22
|
||||
antlrVersion=4.10.1
|
||||
@ -28,7 +29,7 @@ publishChannels=eap
|
||||
|
||||
# Kotlinx serialization also uses some version of kotlin stdlib under the hood. However,
|
||||
# we exclude this version from the dependency and use our own version of kotlin that is specified above
|
||||
kotlinxSerializationVersion=1.5.1
|
||||
kotlinxSerializationVersion=1.6.2
|
||||
|
||||
slackUrl=
|
||||
youtrackToken=
|
||||
@ -41,3 +42,4 @@ kotlin.stdlib.default.dependency=false
|
||||
|
||||
# Disable incremental annotation processing
|
||||
ksp.incremental=false
|
||||
|
||||
|
99092
qodana.sarif.json
99092
qodana.sarif.json
File diff suppressed because one or more lines are too long
@ -21,6 +21,9 @@ exclude:
|
||||
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
|
||||
- src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||
- src/main/java/com/maddyhome/idea/vim/package-info.java
|
||||
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
|
||||
- tests/ui-fixtures
|
||||
dependencyIgnores:
|
||||
- name: "acejump"
|
||||
- name: "icu4j"
|
||||
|
@ -20,17 +20,17 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
|
||||
|
||||
implementation("io.ktor:ktor-client-core:2.3.7")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.7")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.7")
|
||||
implementation("io.ktor:ktor-client-core:2.3.9")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.9")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.9")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.9")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||
implementation("com.vdurmont:semver4j:3.1.0")
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||
*/
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val knownPlugins = listOf(
|
||||
val knownPlugins = setOf(
|
||||
"IdeaVimExtension",
|
||||
"github.zgqq.intellij-enhance",
|
||||
"org.jetbrains.IdeaVim-EasyMotion",
|
||||
@ -31,7 +31,12 @@ val knownPlugins = listOf(
|
||||
"com.github.copilot",
|
||||
"com.github.dankinsoid.multicursor",
|
||||
"com.joshestein.ideavim-quickscope",
|
||||
|
||||
"ca.alexgirard.HarpoonIJ",
|
||||
"me.kyren223.harpoonforjb", // https://plugins.jetbrains.com/plugin/23771-harpoonforjb
|
||||
"com.github.erotourtes.harpoon", // https://plugins.jetbrains.com/plugin/21796-harpooner
|
||||
"me.kyren223.trident", // https://plugins.jetbrains.com/plugin/23818-trident
|
||||
|
||||
"com.protoseo.input-source-auto-converter",
|
||||
|
||||
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
|
||||
@ -42,7 +47,7 @@ suspend fun main() {
|
||||
parameter("dependency", "IdeaVIM")
|
||||
parameter("includeOptional", true)
|
||||
}
|
||||
val output = response.body<List<String>>()
|
||||
val output = response.body<List<String>>().toSet()
|
||||
println(output)
|
||||
if (knownPlugins != output) {
|
||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
|
@ -8,6 +8,12 @@
|
||||
|
||||
package scripts.release
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import org.eclipse.jgit.revwalk.filter.RevFilter
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("HI!")
|
||||
val projectDir = args[0]
|
||||
@ -19,10 +25,12 @@ fun main(args: Array<String>) {
|
||||
check(branch == "master") {
|
||||
"We should be on master branch"
|
||||
}
|
||||
val mergeBaseCommit = getMergeBaseWithMaster(projectDir, objectId)
|
||||
println("Base commit $mergeBaseCommit")
|
||||
withGit(projectDir) { git ->
|
||||
val log = git.log().setMaxCount(500).call().toList()
|
||||
println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
|
||||
val logDiff = log.takeWhile { it.id.name != objectId.name }
|
||||
val logDiff = log.takeWhile { it.id.name != mergeBaseCommit }
|
||||
val numCommits = logDiff.size
|
||||
println("Log diff size is $numCommits")
|
||||
check(numCommits < 450) {
|
||||
@ -35,3 +43,18 @@ fun main(args: Array<String>) {
|
||||
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMergeBaseWithMaster(projectDir: String, tag: ObjectId): String {
|
||||
withRepo(projectDir) { repo ->
|
||||
val master = repo.resolve("master")
|
||||
RevWalk(repo).use { walk ->
|
||||
val tagRevCommit = walk.parseCommit(tag)
|
||||
val masterRevCommit = walk.parseCommit(master)
|
||||
walk.setRevFilter(RevFilter.MERGE_BASE)
|
||||
walk.markStart(tagRevCommit)
|
||||
walk.markStart(masterRevCommit)
|
||||
val mergeBase: RevCommit = walk.next()
|
||||
return mergeBase.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ package com.maddyhome.idea.vim;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.ShortcutSet;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction;
|
||||
@ -80,14 +79,6 @@ public class EventFacade {
|
||||
action.unregisterCustomShortcutSet(component);
|
||||
}
|
||||
|
||||
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||
document.addDocumentListener(listener);
|
||||
}
|
||||
|
||||
public void removeDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||
document.removeDocumentListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
|
||||
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
|
||||
}
|
||||
@ -98,20 +89,12 @@ public class EventFacade {
|
||||
editor.getCaretModel().addCaretListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeCaretListener(@NotNull Editor editor, @NotNull CaretListener listener) {
|
||||
editor.getCaretModel().removeCaretListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorMouseListener(@NotNull Editor editor,
|
||||
@NotNull EditorMouseListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
|
||||
editor.removeEditorMouseListener(listener);
|
||||
}
|
||||
|
||||
public void addComponentMouseListener(@NotNull Component component,
|
||||
@NotNull MouseListener mouseListener,
|
||||
@NotNull Disposable disposable) {
|
||||
@ -119,30 +102,18 @@ public class EventFacade {
|
||||
Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
|
||||
}
|
||||
|
||||
public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
|
||||
component.removeMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
public void addEditorMouseMotionListener(@NotNull Editor editor,
|
||||
@NotNull EditorMouseMotionListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseMotionListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
|
||||
editor.removeEditorMouseMotionListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorSelectionListener(@NotNull Editor editor,
|
||||
@NotNull SelectionListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.getSelectionModel().addSelectionListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
|
||||
editor.getSelectionModel().removeSelectionListener(listener);
|
||||
}
|
||||
|
||||
private @NotNull TypedAction getTypedAction() {
|
||||
return TypedAction.getInstance();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import com.intellij.openapi.project.ProjectManagerListener
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
|
||||
/**
|
||||
@ -36,8 +36,10 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
||||
// This is a temporal workaround for VIM-2487
|
||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
|
||||
override fun projectClosingBeforeSave(project: Project) {
|
||||
// TODO: Confirm context in CWM scenario
|
||||
if (injector.globalIjOptions().closenotebooks) {
|
||||
localEditors().forEach { editor ->
|
||||
injector.editorGroup.getEditors().forEach { vimEditor ->
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val virtualFile = EditorHelper.getVirtualFile(editor)
|
||||
if (virtualFile?.extension == "ipynb") {
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
|
@ -21,7 +21,7 @@ public object RegisterActions {
|
||||
@JvmStatic
|
||||
public fun registerActions() {
|
||||
registerVimCommandActions()
|
||||
registerEmptyShortcuts() // todo most likely it is not needed
|
||||
registerShortcutsWithoutActions()
|
||||
}
|
||||
|
||||
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||
@ -46,12 +46,11 @@ public object RegisterActions {
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
}
|
||||
|
||||
private fun registerEmptyShortcuts() {
|
||||
private fun registerShortcutsWithoutActions() {
|
||||
val parser = VimPlugin.getKey()
|
||||
|
||||
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
|
||||
// still need to register the shortcut, to make sure the editor doesn't swallow it.
|
||||
parser
|
||||
.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
|
||||
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
|
||||
}
|
||||
}
|
||||
|
@ -211,22 +211,22 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
public static void setEnabled(final boolean enabled) {
|
||||
if (isEnabled() == enabled) return;
|
||||
|
||||
if (!enabled) {
|
||||
getInstance().turnOffPlugin(true);
|
||||
}
|
||||
|
||||
getInstance().enabled = enabled;
|
||||
|
||||
if (enabled) {
|
||||
getInstance().turnOnPlugin();
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
|
||||
} else {
|
||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
getInstance().turnOffPlugin(true);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
getInstance().turnOnPlugin();
|
||||
}
|
||||
|
||||
StatusBarIconFactory.Util.INSTANCE.updateIcon();
|
||||
}
|
||||
|
||||
@ -353,6 +353,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
|
||||
if (onOffDisposable != null) {
|
||||
Disposer.dispose(onOffDisposable);
|
||||
onOffDisposable = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||
|
||||
@Service
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class VimProjectService(val project: Project) : Disposable {
|
||||
override fun dispose() {
|
||||
// Not sure if this is a best solution
|
||||
|
@ -77,7 +77,7 @@ public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActio
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
val startTime = if (traceTime) System.currentTimeMillis() else null
|
||||
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim))
|
||||
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim), handler.keyHandlerState)
|
||||
if (startTime != null) {
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
||||
|
@ -44,7 +44,6 @@ import com.maddyhome.idea.vim.listener.AceJumpService
|
||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.KeyEvent
|
||||
@ -79,10 +78,12 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
||||
// Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
|
||||
try {
|
||||
val start = if (traceTime) System.currentTimeMillis() else null
|
||||
KeyHandler.getInstance().handleKey(
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(
|
||||
editor.vim,
|
||||
keyStroke,
|
||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
||||
keyHandler.keyHandlerState,
|
||||
)
|
||||
if (start != null) {
|
||||
val duration = System.currentTimeMillis() - start
|
||||
|
@ -14,6 +14,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
@ -21,6 +22,7 @@ import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.argumentCaptured
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
@ -29,21 +31,67 @@ import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
||||
|
||||
// todo make it multicaret
|
||||
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
|
||||
val operatorFunction = injector.keyGroup.operatorFunction
|
||||
if (operatorFunction == null) {
|
||||
val func = injector.globalOptions().operatorfunc
|
||||
if (func.isEmpty()) {
|
||||
VimPlugin.showMessage(MessageHelper.message("E774"))
|
||||
return false
|
||||
}
|
||||
|
||||
val scriptContext = CommandLineVimLContext
|
||||
|
||||
// The option value is either a function name, which should have a handler, or it might be a lambda expression, or a
|
||||
// `function` or `funcref` call expression, all of which will return a funcref (with a handler)
|
||||
var handler = injector.functionService.getFunctionHandlerOrNull(null, func, scriptContext)
|
||||
if (handler == null) {
|
||||
val expression = injector.vimscriptParser.parseExpression(func)
|
||||
if (expression != null) {
|
||||
try {
|
||||
val value = expression.evaluate(editor, context, scriptContext)
|
||||
if (value is VimFuncref) {
|
||||
handler = value.handler
|
||||
}
|
||||
} catch (ex: ExException) {
|
||||
// Get the argument for function('...') or funcref('...') for the error message
|
||||
val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
|
||||
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
||||
}
|
||||
else {
|
||||
func
|
||||
}
|
||||
|
||||
VimPlugin.showMessage("E117: Unknown function: $functionName")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handler == null) {
|
||||
VimPlugin.showMessage("E117: Unknown function: $func")
|
||||
return false
|
||||
}
|
||||
|
||||
val arg = when (selectionType) {
|
||||
SelectionType.LINE_WISE -> "line"
|
||||
SelectionType.CHARACTER_WISE -> "char"
|
||||
SelectionType.BLOCK_WISE -> "block"
|
||||
}
|
||||
|
||||
val saveRepeatHandler = VimRepeater.repeatHandler
|
||||
injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
|
||||
KeyHandler.getInstance().reset(editor)
|
||||
val result = operatorFunction.apply(editor, context, selectionType)
|
||||
|
||||
val arguments = listOf(SimpleExpression(arg))
|
||||
handler.executeFunction(arguments, editor, context, scriptContext)
|
||||
|
||||
VimRepeater.repeatHandler = saveRepeatHandler
|
||||
return result
|
||||
return true
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])
|
||||
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.change
|
||||
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.vim.annotations.CommandOrMotion
|
||||
import com.intellij.vim.annotations.Mode
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
|
@ -21,19 +21,19 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
public class CommandState(private val machine: VimStateMachine) {
|
||||
|
||||
public val isOperatorPending: Boolean
|
||||
get() = machine.isOperatorPending
|
||||
get() = machine.isOperatorPending(machine.mode)
|
||||
|
||||
public val mode: CommandState.Mode
|
||||
public val mode: Mode
|
||||
get() {
|
||||
val myMode = machine.mode
|
||||
return when (myMode) {
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
|
||||
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
|
||||
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
|
||||
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
|
||||
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,21 +14,34 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.action.change.Extension
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.common.CommandAlias
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.CommandLineHelper
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||
import com.maddyhome.idea.vim.helper.noneOfEnum
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
|
||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
|
||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionFlag
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
@ -120,12 +133,6 @@ public object VimExtensionFacade {
|
||||
.setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
|
||||
}
|
||||
|
||||
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
|
||||
@JvmStatic
|
||||
public fun setOperatorFunction(function: OperatorFunction) {
|
||||
VimPlugin.getKey().operatorFunction = function
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs normal mode commands similar to ':normal! {commands}'.
|
||||
* Mappings doesn't work with this function
|
||||
@ -136,7 +143,8 @@ public object VimExtensionFacade {
|
||||
@JvmStatic
|
||||
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
val context = injector.executionContextManager.onEditor(editor.vim)
|
||||
keys.forEach { KeyHandler.getInstance().handleKey(editor.vim, it, context, false, false) }
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
||||
}
|
||||
|
||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||
@ -152,7 +160,7 @@ public object VimExtensionFacade {
|
||||
LOG.trace("Unit test mode is active")
|
||||
val mappingStack = KeyHandler.getInstance().keyStack
|
||||
mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
|
||||
if (editor.vim.vimStateMachine.isRecording) {
|
||||
if (injector.registerGroup.isRecording) {
|
||||
KeyHandler.getInstance().modalEntryKeys += it
|
||||
}
|
||||
}
|
||||
@ -207,4 +215,65 @@ public object VimExtensionFacade {
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun exportScriptFunction(
|
||||
scope: Scope?,
|
||||
name: String,
|
||||
args: List<String>,
|
||||
defaultArgs: List<Pair<String, Expression>>,
|
||||
hasOptionalArguments: Boolean,
|
||||
flags: EnumSet<FunctionFlag>,
|
||||
function: ScriptFunction
|
||||
) {
|
||||
var functionDeclaration: FunctionDeclaration? = null
|
||||
val body = listOf(object : Executable {
|
||||
// This context is set to the function declaration during initialisation and then set to the function execution
|
||||
// context during execution
|
||||
override lateinit var vimContext: VimLContext
|
||||
override var rangeInScript: TextRange = TextRange(0, 0)
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
|
||||
return function.execute(editor, context, functionDeclaration!!.functionVariables)
|
||||
}
|
||||
})
|
||||
functionDeclaration = FunctionDeclaration(
|
||||
scope,
|
||||
name,
|
||||
args,
|
||||
defaultArgs,
|
||||
body,
|
||||
replaceExisting = true,
|
||||
flags,
|
||||
hasOptionalArguments
|
||||
)
|
||||
functionDeclaration.rangeInScript = TextRange(0, 0)
|
||||
body.forEach { it.vimContext = functionDeclaration }
|
||||
injector.functionService.storeFunction(functionDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
||||
editor, context, args ->
|
||||
|
||||
val type = args["type"]?.asString()
|
||||
val selectionType = when (type) {
|
||||
"line" -> SelectionType.LINE_WISE
|
||||
"block" -> SelectionType.BLOCK_WISE
|
||||
"char" -> SelectionType.CHARACTER_WISE
|
||||
else -> return@exportScriptFunction ExecutionResult.Error
|
||||
}
|
||||
|
||||
if (function.apply(editor, context, selectionType)) {
|
||||
ExecutionResult.Success
|
||||
}
|
||||
else {
|
||||
ExecutionResult.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun interface ScriptFunction {
|
||||
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||
}
|
@ -67,7 +67,7 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) {
|
||||
if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) {
|
||||
initExtension(extensionBean, name)
|
||||
PluginState.enabledExtensions.add(name)
|
||||
PluginState.Util.enabledExtensions.add(name)
|
||||
} else {
|
||||
extensionBean.instance.dispose()
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
||||
if (range != null) {
|
||||
|
@ -22,26 +22,26 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
@ -49,17 +49,19 @@ import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import java.util.*
|
||||
|
||||
internal class CommentaryExtension : VimExtension {
|
||||
|
||||
companion object {
|
||||
object Util {
|
||||
fun doCommentary(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
selectionType: SelectionType,
|
||||
resetCaret: Boolean,
|
||||
resetCaret: Boolean = true,
|
||||
): Boolean {
|
||||
val mode = editor.vimStateMachine.mode
|
||||
if (mode !is Mode.VISUAL) {
|
||||
@ -67,8 +69,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
}
|
||||
|
||||
return runWriteAction {
|
||||
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
|
||||
// isn't available
|
||||
// Treat block- and character-wise selections as block comments. Fall back if the first action isn't available
|
||||
val actions = if (selectionType === SelectionType.LINE_WISE) {
|
||||
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
|
||||
} else {
|
||||
@ -113,12 +114,17 @@ internal class CommentaryExtension : VimExtension {
|
||||
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
|
||||
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
|
||||
// the difference
|
||||
// TODO: If we don't move the caret to the start offset, we should maintain the current logical position
|
||||
if (resetCaret) {
|
||||
editor.primaryCaret().moveToOffset(range.startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OPERATOR_FUNC = "CommentaryOperatorFunc"
|
||||
}
|
||||
|
||||
override fun getName() = "commentary"
|
||||
|
||||
override fun init() {
|
||||
@ -145,6 +151,16 @@ internal class CommentaryExtension : VimExtension {
|
||||
putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
|
||||
|
||||
addCommand("Commentary", CommentaryCommandAliasHandler())
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, CommentaryOperatorFunction())
|
||||
}
|
||||
|
||||
private class CommentaryOperatorFunction : OperatorFunction {
|
||||
// todo make it multicaret
|
||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
|
||||
return Util.doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,19 +169,13 @@ internal class CommentaryExtension : VimExtension {
|
||||
* E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
|
||||
* invoke the operator. This object is both the mapping handler and the operator function.
|
||||
*/
|
||||
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
|
||||
private class CommentaryOperatorHandler : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(this)
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
|
||||
// todo make it multicaret
|
||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
|
||||
return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
|
||||
}
|
||||
}
|
||||
|
||||
private class CommentaryMappingHandler : ExtensionHandler {
|
||||
@ -239,7 +249,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
*/
|
||||
private class CommentaryCommandAliasHandler : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||
Util.doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,24 +19,22 @@ import com.intellij.openapi.util.Key
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
@ -45,6 +43,8 @@ import com.maddyhome.idea.vim.mark.VimMarkConstants
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
@ -72,30 +72,13 @@ internal class VimExchangeExtension : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls
|
||||
const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||
|
||||
@NonNls
|
||||
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||
|
||||
@NonNls
|
||||
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||
|
||||
object Util {
|
||||
val EXCHANGE_KEY = Key<Exchange>("exchange")
|
||||
|
||||
// End mark has always greater of eq offset than start mark
|
||||
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
|
||||
private var myHighlighter: RangeHighlighter? = null
|
||||
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||
myHighlighter = highlighter
|
||||
}
|
||||
|
||||
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||
}
|
||||
|
||||
fun clearExchange(editor: Editor) {
|
||||
editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
|
||||
editor.markupModel.removeHighlighter(it)
|
||||
@ -104,18 +87,25 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls private const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||
@NonNls private const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||
@NonNls private const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||
@NonNls private const val OPERATOR_FUNC = "ExchangeOperatorFunc"
|
||||
}
|
||||
|
||||
private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator(false))
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangeClearHandler : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
clearExchange(editor.ij)
|
||||
Util.clearExchange(editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,12 +115,12 @@ internal class VimExchangeExtension : VimExtension {
|
||||
val mode = editor.mode
|
||||
// Leave visual mode to create selection marks
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
||||
Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
|
||||
Operator(true).apply(editor, context, mode.selectionType ?: SelectionType.CHARACTER_WISE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Operator(private val isVisual: Boolean) : OperatorFunction {
|
||||
private class Operator(private val isVisual: Boolean = false) : OperatorFunction {
|
||||
fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
|
||||
fun SelectionType.getString() = when (this) {
|
||||
SelectionType.CHARACTER_WISE -> "v"
|
||||
@ -148,7 +138,7 @@ internal class VimExchangeExtension : VimExtension {
|
||||
else -> HighlighterTargetArea.EXACT_RANGE
|
||||
}
|
||||
val isVisualLine = ex.type == SelectionType.LINE_WISE
|
||||
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0
|
||||
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || isVisual)) 1 else 0
|
||||
return ijEditor.markupModel.addRangeHighlighter(
|
||||
ijEditor.getMarkOffset(ex.start),
|
||||
(ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
|
||||
@ -158,12 +148,12 @@ internal class VimExchangeExtension : VimExtension {
|
||||
)
|
||||
}
|
||||
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(Util.EXCHANGE_KEY)
|
||||
if (exchange1 == null) {
|
||||
val highlighter = highlightExchange(currentExchange)
|
||||
currentExchange.setHighlighter(highlighter)
|
||||
ijEditor.putUserData(EXCHANGE_KEY, currentExchange)
|
||||
ijEditor.putUserData(Util.EXCHANGE_KEY, currentExchange)
|
||||
return true
|
||||
} else {
|
||||
val cmp = compareExchanges(exchange1, currentExchange)
|
||||
@ -189,7 +179,7 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
exchange(ijEditor, ex1, ex2, reverse, expand)
|
||||
clearExchange(ijEditor)
|
||||
Util.clearExchange(ijEditor)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -354,4 +344,14 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End mark has always greater of eq offset than start mark
|
||||
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
|
||||
private var myHighlighter: RangeHighlighter? = null
|
||||
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||
myHighlighter = highlighter
|
||||
}
|
||||
|
||||
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ internal class Matchit : VimExtension {
|
||||
|
||||
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
||||
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
||||
val isInOpPending = commandState.isOperatorPending
|
||||
val isInOpPending = commandState.isOperatorPending(editor.mode)
|
||||
|
||||
if (isInOpPending) {
|
||||
val matchitAction = MatchitAction()
|
||||
|
@ -130,15 +130,15 @@ internal class NerdTree : VimExtension {
|
||||
addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
|
||||
addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
|
||||
|
||||
synchronized(monitor) {
|
||||
commandsRegistered = true
|
||||
synchronized(Util.monitor) {
|
||||
Util.commandsRegistered = true
|
||||
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
|
||||
}
|
||||
}
|
||||
|
||||
class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
callAction(editor, actionId, context)
|
||||
Util.callAction(editor, actionId, context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +149,7 @@ internal class NerdTree : VimExtension {
|
||||
if (toolWindow.isVisible) {
|
||||
toolWindow.hide()
|
||||
} else {
|
||||
callAction(editor, "ActivateProjectToolWindow", context)
|
||||
Util.callAction(editor, "ActivateProjectToolWindow", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,8 +187,8 @@ internal class NerdTree : VimExtension {
|
||||
// TODO I'm not sure is this activity runs at all? Should we use [RunOnceUtil] instead?
|
||||
class NerdStartupActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
synchronized(monitor) {
|
||||
if (!commandsRegistered) return
|
||||
synchronized(Util.monitor) {
|
||||
if (!Util.commandsRegistered) return
|
||||
installDispatcher(project)
|
||||
}
|
||||
}
|
||||
@ -214,7 +214,7 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
val action = nextNode.actionHolder
|
||||
when (action) {
|
||||
is NerdAction.ToIj -> callAction(null, action.name, e.dataContext.vim)
|
||||
is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
|
||||
is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
|
||||
}
|
||||
}
|
||||
@ -356,7 +356,7 @@ internal class NerdTree : VimExtension {
|
||||
currentWindow?.split(SwingConstants.VERTICAL, true, file, true)
|
||||
|
||||
// FIXME: 22.01.2021 This solution bouncing a bit
|
||||
callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
},
|
||||
)
|
||||
registerCommand(
|
||||
@ -368,7 +368,7 @@ internal class NerdTree : VimExtension {
|
||||
val currentWindow = splitters.currentWindow
|
||||
currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
|
||||
|
||||
callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
},
|
||||
)
|
||||
registerCommand(
|
||||
@ -511,14 +511,9 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
|
||||
object Util {
|
||||
internal val monitor = Any()
|
||||
internal var commandsRegistered = false
|
||||
|
||||
private val LOG = logger<NerdTree>()
|
||||
|
||||
fun callAction(editor: VimEditor?, name: String, context: ExecutionContext) {
|
||||
val action = ActionManager.getInstance().getAction(name) ?: run {
|
||||
VimPlugin.showMessage(MessageHelper.message("action.not.found.0", name))
|
||||
@ -533,45 +528,51 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||
}
|
||||
|
||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
|
||||
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||
val mappings = if (variableValue is VimString) {
|
||||
variableValue.value
|
||||
} else {
|
||||
default
|
||||
}
|
||||
actionsRoot.addLeafs(mappings, action)
|
||||
}
|
||||
|
||||
private fun registerCommand(default: String, action: NerdAction) {
|
||||
actionsRoot.addLeafs(default, action)
|
||||
}
|
||||
|
||||
private val actionsRoot: RootNode<NerdAction> = RootNode()
|
||||
private var currentNode: CommandPartNode<NerdAction> = actionsRoot
|
||||
|
||||
private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
|
||||
return if (node is CommandPartNode<NerdAction>) {
|
||||
val res = node.keys.toMutableSet()
|
||||
res += node.values.map { collectShortcuts(it) }.flatten()
|
||||
res
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun installDispatcher(project: Project) {
|
||||
val dispatcher = NerdDispatcher.getInstance(project)
|
||||
val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
|
||||
dispatcher.registerCustomShortcutSet(
|
||||
KeyGroup.toShortcutSet(shortcuts),
|
||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
||||
)
|
||||
}
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
private val LOG = logger<NerdTree>()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||
}
|
||||
|
||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
|
||||
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||
val mappings = if (variableValue is VimString) {
|
||||
variableValue.value
|
||||
} else {
|
||||
default
|
||||
}
|
||||
actionsRoot.addLeafs(mappings, action)
|
||||
}
|
||||
|
||||
private fun registerCommand(default: String, action: NerdAction) {
|
||||
actionsRoot.addLeafs(default, action)
|
||||
}
|
||||
|
||||
|
||||
private val actionsRoot: RootNode<NerdAction> = RootNode()
|
||||
private var currentNode: CommandPartNode<NerdAction> = actionsRoot
|
||||
private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
|
||||
return if (node is CommandPartNode<NerdAction>) {
|
||||
val res = node.keys.toMutableSet()
|
||||
res += node.values.map { collectShortcuts(it) }.flatten()
|
||||
res
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun installDispatcher(project: Project) {
|
||||
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
||||
val shortcuts =
|
||||
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
||||
dispatcher.registerCustomShortcutSet(
|
||||
KeyGroup.toShortcutSet(shortcuts),
|
||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
||||
)
|
||||
}
|
||||
|
@ -9,10 +9,11 @@
|
||||
package com.maddyhome.idea.vim.extension.paragraphmotion
|
||||
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
@ -20,8 +21,10 @@ import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.helper.vimForEachCaret
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
internal class ParagraphMotion : VimExtension {
|
||||
override fun getName(): String = "vim-paragraph-motion"
|
||||
@ -30,8 +33,8 @@ internal class ParagraphMotion : VimExtension {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), owner, ParagraphMotionHandler(1), false)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), owner, ParagraphMotionHandler(-1), false)
|
||||
|
||||
putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
|
||||
putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
|
||||
putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
|
||||
putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
|
||||
}
|
||||
|
||||
private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler {
|
||||
@ -45,7 +48,21 @@ internal class ParagraphMotion : VimExtension {
|
||||
}
|
||||
|
||||
fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? {
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)?.let(editor::getLineEndForOffset)
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)
|
||||
?.let { editor.normalizeOffset(it, true) }
|
||||
}
|
||||
}
|
||||
|
||||
// For VIM-3306
|
||||
@Suppress("SameParameterValue")
|
||||
private fun putKeyMappingIfMissingFromAndToKeys(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
toKeys: List<KeyStroke>,
|
||||
recursive: Boolean,
|
||||
) {
|
||||
val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().hasmapfrom(it, fromKeys) }
|
||||
putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
|
||||
}
|
||||
}
|
||||
|
@ -14,24 +14,19 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
|
||||
import com.maddyhome.idea.vim.state.mode.isLine
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
@ -39,6 +34,10 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
||||
import com.maddyhome.idea.vim.put.PutData
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.isLine
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
internal class ReplaceWithRegister : VimExtension {
|
||||
@ -53,11 +52,13 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true)
|
||||
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true)
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
||||
}
|
||||
|
||||
private class RwrVisual : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE
|
||||
val typeInEditor = editor.mode.selectionType ?: SelectionType.CHARACTER_WISE
|
||||
editor.sortedCarets().forEach { caret ->
|
||||
val selectionStart = caret.selectionStart
|
||||
val selectionEnd = caret.selectionEnd
|
||||
@ -73,7 +74,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
override val isRepeatable: Boolean = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator())
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@ -112,11 +113,11 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
editor.primaryCaret() to VimSelection.create(
|
||||
range.startOffset,
|
||||
range.endOffset - 1,
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
editor,
|
||||
),
|
||||
),
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
)
|
||||
// todo multicaret
|
||||
doReplace(ijEditor, editor.primaryCaret(), visualSelection)
|
||||
@ -132,52 +133,49 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls
|
||||
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||
|
||||
@NonNls
|
||||
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||
|
||||
@NonNls
|
||||
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||
|
||||
private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||
val registerGroup = injector.registerGroup
|
||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||
// Code from original plugin implementation. Correct text for linewise selected text
|
||||
usedText = usedText.dropLast(1)
|
||||
usedType = SelectionType.CHARACTER_WISE
|
||||
}
|
||||
|
||||
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
|
||||
|
||||
val putData = PutData(
|
||||
textData,
|
||||
visualSelection,
|
||||
1,
|
||||
insertTextBeforeCaret = true,
|
||||
rawIndent = true,
|
||||
caretAfterInsertedText = false,
|
||||
putToLine = -1,
|
||||
)
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
IjVimEditor(editor),
|
||||
injector.executionContextManager.onEditor(editor.vim),
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending ?: false,
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
||||
@NonNls private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||
@NonNls private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||
@NonNls private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||
@NonNls private const val OPERATOR_FUNC = "ReplaceWithRegisterOperatorFunc"
|
||||
}
|
||||
}
|
||||
|
||||
private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||
val registerGroup = injector.registerGroup
|
||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||
// Code from original plugin implementation. Correct text for linewise selected text
|
||||
usedText = usedText.dropLast(1)
|
||||
usedType = SelectionType.CHARACTER_WISE
|
||||
}
|
||||
|
||||
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
|
||||
|
||||
val putData = PutData(
|
||||
textData,
|
||||
visualSelection,
|
||||
1,
|
||||
insertTextBeforeCaret = true,
|
||||
rawIndent = true,
|
||||
caretAfterInsertedText = false,
|
||||
putToLine = -1,
|
||||
)
|
||||
val vimEditor = editor.vim
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
vimEditor,
|
||||
injector.executionContextManager.onEditor(editor.vim),
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.helper.StrictMode
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import java.awt.Font
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.*
|
||||
import javax.swing.Timer
|
||||
|
||||
|
||||
@ -48,17 +49,20 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
|
||||
override fun init() {
|
||||
val highlightHandler = HighlightHandler()
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
|
||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
|
||||
|
||||
// vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
|
||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.NO)
|
||||
mapToFunctionAndProvideKeys("Z", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.X)
|
||||
|
||||
// workaround to support ; and , commands
|
||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
|
||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
|
||||
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
||||
}
|
||||
|
||||
private class SneakHandler(
|
||||
@ -273,16 +277,18 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
* Map some <Plug>(keys) command to given handler
|
||||
* and create mapping to <Plug>(prefix)[keys]
|
||||
*/
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(
|
||||
keys: String, handler: ExtensionHandler, mappingModes: EnumSet<MappingMode>
|
||||
) {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
MappingMode.NXO,
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
MappingMode.NXO,
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
|
||||
owner,
|
||||
handler,
|
||||
@ -294,17 +300,17 @@ private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: Exte
|
||||
// - The shortcut should not be registered if any of these mappings is overridden in .ideavimrc
|
||||
// - The shortcut should not be registered if some other shortcut for this key exists
|
||||
val fromKeys = injector.parser.parseKeys(keys)
|
||||
val filteredModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredModes = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
|
||||
}
|
||||
val filteredModes2 = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredModes2 = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
||||
}
|
||||
val filteredFromModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
||||
injector.keyGroup.hasmapfrom(it, fromKeys)
|
||||
}
|
||||
|
||||
val doubleFiltered = MappingMode.NXO
|
||||
val doubleFiltered = mappingModes
|
||||
.filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
|
||||
.toSet()
|
||||
putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
|
||||
|
@ -17,6 +17,7 @@ import com.maddyhome.idea.vim.api.VimChangeGroup
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.endsWithNewLine
|
||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
@ -24,14 +25,15 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
@ -78,13 +80,15 @@ internal class VimSurroundExtension : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true)
|
||||
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
||||
}
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||
}
|
||||
|
||||
private class YSurroundHandler : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@ -315,7 +319,9 @@ private val LOG = logger<VimSurroundExtension>()
|
||||
|
||||
private const val REGISTER = '"'
|
||||
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
private const val OPERATOR_FUNC = "SurroundOperatorFunc"
|
||||
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
|
||||
private val SURROUND_PAIRS = mapOf(
|
||||
'b' to ("(" to ")"),
|
||||
|
@ -138,7 +138,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
|
@ -267,7 +267,7 @@ public class VimIndentObject implements VimExtension {
|
||||
|
||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
|
@ -68,10 +68,11 @@ import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.regexp.VimRegex
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.math.BigInteger
|
||||
@ -573,48 +574,62 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
val startOffset = editor.getLineStartOffset(startLine)
|
||||
val endOffset = editor.getLineEndOffset(endLine)
|
||||
return sortTextRange(editor, caret, startOffset, endOffset, lineComparator, sortOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a text range with a comparator. Returns true if a replace was performed, false otherwise.
|
||||
*
|
||||
* @param editor The editor to replace text in
|
||||
* @param start The starting position for the sort
|
||||
* @param end The ending position for the sort
|
||||
* @param lineComparator The comparator to use to sort
|
||||
* @param sortOption The option to sort the range
|
||||
* @return true if able to sort the text, false if not
|
||||
*/
|
||||
private fun sortTextRange(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
start: Int,
|
||||
end: Int,
|
||||
lineComparator: Comparator<String>,
|
||||
sortOption: SortOption,
|
||||
): Boolean {
|
||||
val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(start, end))
|
||||
val lines: MutableList<String> = selectedText.split("\n").sortedWith(lineComparator).toMutableList()
|
||||
if (sortOption.unique) {
|
||||
val iterator = lines.iterator()
|
||||
val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(startOffset, endOffset))
|
||||
val lines = selectedText.split("\n")
|
||||
val modifiedLines = sortOptions.pattern?.let {
|
||||
if (sortOptions.sortOnPattern) {
|
||||
extractPatternFromLines(editor, lines, startLine, it)
|
||||
} else {
|
||||
deletePatternFromLines(editor, lines, startLine, it)
|
||||
}
|
||||
} ?: lines
|
||||
val sortedLines = lines.zip(modifiedLines)
|
||||
.sortedWith { l1, l2 -> lineComparator.compare(l1.second, l2.second) }
|
||||
.map {it.first}
|
||||
.toMutableList()
|
||||
|
||||
if (sortOptions.unique) {
|
||||
val iterator = sortedLines.iterator()
|
||||
var previous: String? = null
|
||||
while (iterator.hasNext()) {
|
||||
val current = iterator.next()
|
||||
if (current == previous || sortOption.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
previous = current
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lines.size < 1) {
|
||||
if (sortedLines.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
replaceText(editor, caret, start, end, StringUtil.join(lines, "\n"))
|
||||
replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
|
||||
return true
|
||||
}
|
||||
|
||||
private fun extractPatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
|
||||
val regex = VimRegex(pattern)
|
||||
return lines.mapIndexed { i: Int, line: String ->
|
||||
val result = regex.findInLine(editor, startLine + i, 0)
|
||||
when (result) {
|
||||
is VimMatchResult.Success -> result.value
|
||||
is VimMatchResult.Failure -> line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deletePatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
|
||||
val regex = VimRegex(pattern)
|
||||
return lines.mapIndexed { i: Int, line: String ->
|
||||
val result = regex.findInLine(editor, startLine + i, 0)
|
||||
when (result) {
|
||||
is VimMatchResult.Success -> line.substring(result.value.length, line.length)
|
||||
is VimMatchResult.Failure -> line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform increment and decrement for numbers in visual mode
|
||||
*
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.maddyhome.idea.vim.api.VimCommandGroupBase
|
||||
|
||||
/**
|
||||
* @author Elliot Courant
|
||||
*/
|
||||
@Service
|
||||
internal class CommandGroup : VimCommandGroupBase()
|
||||
|
@ -11,6 +11,9 @@ package com.maddyhome.idea.vim.group;
|
||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
||||
import com.intellij.find.EditorSearchSession;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.client.ClientAppSession;
|
||||
import com.intellij.openapi.client.ClientKind;
|
||||
import com.intellij.openapi.client.ClientSessionsManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
@ -22,7 +25,10 @@ import com.intellij.openapi.project.Project;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.helper.*;
|
||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener;
|
||||
@ -34,10 +40,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
||||
import static com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt.updateCaretsVisualAttributes;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
||||
|
||||
/**
|
||||
@ -204,7 +210,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
|
||||
public void editorCreated(@NotNull Editor editor) {
|
||||
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
||||
UserDataManager.setVimInitialised(editor, true);
|
||||
|
||||
VimPlugin.getKey().registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
|
||||
initLineNumbers(editor);
|
||||
@ -246,14 +253,13 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
switchToInsertMode.run();
|
||||
}
|
||||
});
|
||||
updateCaretsVisualAttributes(editor);
|
||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||
}
|
||||
|
||||
public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
|
||||
deinitLineNumbers(editor, isReleased);
|
||||
UserDataManager.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
||||
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
||||
}
|
||||
|
||||
@ -284,6 +290,18 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
notifyIdeaJoin(((IjVimEditor) editor).getEditor().getProject(), editor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCaretsVisualAttributes(@NotNull VimEditor editor) {
|
||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCaretsVisualPosition(@NotNull VimEditor editor) {
|
||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||
}
|
||||
|
||||
public static class NumberChangeListener implements EffectiveOptionValueChangeListener {
|
||||
public static NumberChangeListener INSTANCE = new NumberChangeListener();
|
||||
|
||||
@ -324,20 +342,45 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> localEditors() {
|
||||
return HelperKt.localEditors().stream()
|
||||
public @NotNull Collection<VimEditor> getEditorsRaw() {
|
||||
return getLocalEditors()
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> localEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return HelperKt.localEditors(document).stream()
|
||||
public Collection<VimEditor> getEditors() {
|
||||
return getLocalEditors()
|
||||
.filter(UserDataManager::getVimInitialised)
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return getLocalEditors()
|
||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Stream<Editor> getLocalEditors() {
|
||||
// Always fetch local editors. If we're hosting a Code With Me session, any connected guests will create hidden
|
||||
// editors to handle syntax highlighting, completion requests, etc. We need to make sure that IdeaVim only makes
|
||||
// changes (e.g. adding search highlights) to local editors, so things don't incorrectly flow through to any Clients.
|
||||
// In non-CWM scenarios, or if IdeaVim is installed on the Client, there are only ever local editors, so this will
|
||||
// also work there. In Gateway remote development scenarios, IdeaVim should not be installed on the host, only the
|
||||
// Client, so all should work there too.
|
||||
// Note that most IdeaVim operations are in response to interactive keystrokes, which would mean that
|
||||
// ClientEditorManager.getCurrentInstance would return local editors. However, some operations are in response to
|
||||
// events such as document change (to update search highlights) and these can come from CWM guests, and we'd get the
|
||||
// remote editors.
|
||||
// This invocation will always get local editors, regardless of current context.
|
||||
final ClientAppSession localSession = ClientSessionsManager.getAppSessions(ClientKind.LOCAL).get(0);
|
||||
return localSession.getService(ClientEditorManager.class).editors();
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,6 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelperRt;
|
||||
@ -40,6 +38,8 @@ import com.maddyhome.idea.vim.helper.SearchHelper;
|
||||
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -438,14 +438,11 @@ public class FileGroup extends VimFileBase {
|
||||
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||
|
||||
/**
|
||||
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
*/
|
||||
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
|
||||
// The user has changed the editor they are working with - exit insert/replace mode, and complete any
|
||||
// appropriate repeat
|
||||
if (event.getOldFile() != null) {
|
||||
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
public var ide: String by optionProperty(IjOptions.ide)
|
||||
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||
@ -29,15 +28,15 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
|
||||
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||
public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
|
||||
|
||||
// Temporary options to control work-in-progress behaviour
|
||||
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,8 +33,6 @@ public object IjOptions {
|
||||
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
|
||||
}
|
||||
|
||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true))
|
||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true))
|
||||
public val ide: StringOption = addOption(
|
||||
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
||||
)
|
||||
@ -81,13 +79,16 @@ public object IjOptions {
|
||||
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
||||
)
|
||||
public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true))
|
||||
public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true))
|
||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true))
|
||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true))
|
||||
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true))
|
||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isTemporary = true))
|
||||
|
||||
// Temporary feature flags during development, not really intended for external use
|
||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||
|
||||
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
||||
// derives from Option<VimInt>
|
||||
|
@ -15,38 +15,38 @@ import com.maddyhome.idea.vim.statistic.VimscriptState
|
||||
internal class IjStatisticsService : VimStatistics {
|
||||
|
||||
override fun logTrackedAction(actionId: String) {
|
||||
ActionTracker.logTrackedAction(actionId)
|
||||
ActionTracker.Util.logTrackedAction(actionId)
|
||||
}
|
||||
|
||||
override fun logCopiedAction(actionId: String) {
|
||||
ActionTracker.logCopiedAction(actionId)
|
||||
ActionTracker.Util.logCopiedAction(actionId)
|
||||
}
|
||||
|
||||
override fun setIfLoopUsed(value: Boolean) {
|
||||
VimscriptState.isLoopUsed = value
|
||||
VimscriptState.Util.isLoopUsed = value
|
||||
}
|
||||
|
||||
override fun setIfMapExprUsed(value: Boolean) {
|
||||
VimscriptState.isMapExprUsed = value
|
||||
VimscriptState.Util.isMapExprUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionCallUsed(value: Boolean) {
|
||||
VimscriptState.isFunctionCallUsed = value
|
||||
VimscriptState.Util.isFunctionCallUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionDeclarationUsed(value: Boolean) {
|
||||
VimscriptState.isFunctionDeclarationUsed = value
|
||||
VimscriptState.Util.isFunctionDeclarationUsed = value
|
||||
}
|
||||
|
||||
override fun setIfIfUsed(value: Boolean) {
|
||||
VimscriptState.isIfUsed = value
|
||||
VimscriptState.Util.isIfUsed = value
|
||||
}
|
||||
|
||||
override fun addExtensionEnabledWithPlug(extension: String) {
|
||||
VimscriptState.extensionsEnabledWithPlug.add(extension)
|
||||
VimscriptState.Util.extensionsEnabledWithPlug.add(extension)
|
||||
}
|
||||
|
||||
override fun addSourcedFile(path: String) {
|
||||
VimscriptState.sourcedFiles.add(path)
|
||||
VimscriptState.Util.sourcedFiles.add(path)
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,12 @@ import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
|
||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.api.NativeAction;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.HelperKt;
|
||||
import com.maddyhome.idea.vim.key.*;
|
||||
import com.maddyhome.idea.vim.newapi.IjNativeAction;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
@ -99,9 +101,9 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
|
||||
@Override
|
||||
public void updateShortcutKeysRegistration() {
|
||||
for (Editor editor : HelperKt.localEditors()) {
|
||||
unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
||||
unregisterShortcutKeys(editor);
|
||||
registerRequiredShortcutKeys(editor);
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,7 +230,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
|
||||
for (KeyStroke key : keys) {
|
||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
||||
if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
|
||||
if (!injector.getApplication().isOctopusEnabled() ||
|
||||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
|
||||
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
|
||||
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
|
||||
|
@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.group
|
||||
import com.intellij.codeInsight.completion.CompletionPhase
|
||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
@ -26,6 +27,7 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
/**
|
||||
* Used to handle playback of macros
|
||||
*/
|
||||
@Service
|
||||
internal class MacroGroup : VimMacroBase() {
|
||||
|
||||
// If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro
|
||||
@ -76,11 +78,12 @@ internal class MacroGroup : VimMacroBase() {
|
||||
} catch (e: ProcessCanceledException) {
|
||||
return@runnable
|
||||
}
|
||||
val keyHandler = getInstance()
|
||||
ProgressManager.getInstance().executeNonCancelableSection {
|
||||
// Prevent autocompletion during macros.
|
||||
// See https://github.com/JetBrains/ideavim/pull/772 for details
|
||||
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion)
|
||||
getInstance().handleKey(editor, key, context)
|
||||
keyHandler.handleKey(editor, key, context, keyHandler.keyHandlerState)
|
||||
}
|
||||
if (injector.messages.isError()) return@runnable
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
@ -83,6 +84,7 @@ import kotlin.math.min
|
||||
/**
|
||||
* This handles all motion related commands and marks
|
||||
*/
|
||||
@Service
|
||||
internal class MotionGroup : VimMotionGroupBase() {
|
||||
override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
|
||||
AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
|
||||
|
@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.openapi.keymap.KeymapUtil
|
||||
@ -55,6 +56,7 @@ import javax.swing.KeyStroke
|
||||
* This service is can be used as application level and as project level service.
|
||||
* If project is null, this means that this is an application level service and notification will be shown for all projects
|
||||
*/
|
||||
@Service(Service.Level.PROJECT, Service.Level.APP)
|
||||
internal class NotificationService(private val project: Project?) {
|
||||
// This constructor is used to create an applicationService
|
||||
@Suppress("unused")
|
||||
@ -276,7 +278,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
ActionTracker.logTrackedAction(id)
|
||||
ActionTracker.Util.logTrackedAction(id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +286,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
|
||||
if (id != null) {
|
||||
ActionTracker.logCopiedAction(id)
|
||||
ActionTracker.Util.logCopiedAction(id)
|
||||
}
|
||||
notification?.expire()
|
||||
|
||||
|
@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.util.execution.ParametersListUtil
|
||||
import com.intellij.util.text.CharSequenceReader
|
||||
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.KeyProcessResult
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
@ -37,7 +38,6 @@ import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.NORMAL
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||
import java.io.BufferedWriter
|
||||
@ -85,24 +85,27 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
modeBeforeCommandProcessing = currentMode
|
||||
val initText = getRange(editor, cmd)
|
||||
injector.markService.setVisualSelectionMarks(editor)
|
||||
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||
}
|
||||
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean {
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
||||
// is open. So I'll put focus back in the editor and process the key.
|
||||
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
if (panel.isActive) {
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
|
||||
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
getInstance(editor).mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||
lambdaEditor.mode = NORMAL()
|
||||
getInstance().reset(lambdaEditor)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -112,7 +115,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
panel.deactivate(true)
|
||||
var res = true
|
||||
try {
|
||||
getInstance(editor).mode = NORMAL()
|
||||
editor.mode = NORMAL()
|
||||
|
||||
logger.debug("processing command")
|
||||
|
||||
@ -152,7 +155,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
}
|
||||
|
||||
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
|
||||
editor.vimStateMachine.mode = NORMAL()
|
||||
editor.mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.deactivate(true, resetCaret)
|
||||
@ -162,7 +165,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
val initText = getRange(editor, cmd) + "!"
|
||||
val currentMode = editor.mode
|
||||
check(currentMode is ReturnableFromCmd) { "Cannot enable cmd mode from $currentMode" }
|
||||
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||
import com.maddyhome.idea.vim.register.Register;
|
||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -37,6 +37,10 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
|
||||
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
|
||||
|
||||
public RegisterGroup() {
|
||||
this.initClipboardOptionListener();
|
||||
}
|
||||
|
||||
public void saveData(final @NotNull Element element) {
|
||||
logger.debug("Save registers data");
|
||||
final Element registersElement = new Element("registers");
|
||||
|
@ -21,8 +21,6 @@ import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
@ -33,12 +31,11 @@ import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange;
|
||||
import com.maddyhome.idea.vim.helper.*;
|
||||
import com.maddyhome.idea.vim.history.HistoryConstants;
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
||||
import com.maddyhome.idea.vim.newapi.*;
|
||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
|
||||
import com.maddyhome.idea.vim.regexp.*;
|
||||
import com.maddyhome.idea.vim.regexp.CharPointer;
|
||||
import com.maddyhome.idea.vim.regexp.CharacterClasses;
|
||||
import com.maddyhome.idea.vim.regexp.RegExp;
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext;
|
||||
@ -59,7 +56,6 @@ import java.text.ParsePosition;
|
||||
import java.util.*;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
|
||||
import static com.maddyhome.idea.vim.helper.HelperKt.localEditors;
|
||||
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
|
||||
import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
|
||||
@ -1200,47 +1196,50 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
public static DocumentSearchListener INSTANCE = new DocumentSearchListener();
|
||||
|
||||
@Contract(pure = true)
|
||||
private DocumentSearchListener () {
|
||||
private DocumentSearchListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(@NotNull DocumentEvent event) {
|
||||
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
|
||||
final Document document = event.getDocument();
|
||||
// Loop over all local editors for the changed document, across all projects, and update search highlights.
|
||||
// Note that the change may have come from a remote guest in Code With Me scenarios (in which case
|
||||
// ClientId.current will be a guest ID), but we don't care - we still need to add/remove highlights for the
|
||||
// changed text. Make sure we only update local editors, though.
|
||||
final Document document = event.getDocument();
|
||||
for (VimEditor vimEditor : injector.getEditorGroup().getEditors(new IjVimDocument(document))) {
|
||||
final Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
if (hls == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Editor editor : localEditors(document, project)) {
|
||||
Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
if (hls == null) {
|
||||
continue;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("hls=" + hls);
|
||||
logger.debug("event=" + event);
|
||||
}
|
||||
|
||||
// We can only re-highlight whole lines, so clear any highlights in the affected lines
|
||||
final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
|
||||
final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
|
||||
final int startLineOffset = document.getLineStartOffset(startPosition.line);
|
||||
final int endLineOffset = document.getLineEndOffset(endPosition.line);
|
||||
|
||||
final Iterator<RangeHighlighter> iter = hls.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RangeHighlighter highlighter = iter.next();
|
||||
if (!highlighter.isValid() ||
|
||||
(highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
|
||||
iter.remove();
|
||||
editor.getMarkupModel().removeHighlighter(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("hls=" + hls);
|
||||
logger.debug("event=" + event);
|
||||
}
|
||||
VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
|
||||
|
||||
// We can only re-highlight whole lines, so clear any highlights in the affected lines
|
||||
final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
|
||||
final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
|
||||
final int startLineOffset = document.getLineStartOffset(startPosition.line);
|
||||
final int endLineOffset = document.getLineEndOffset(endPosition.line);
|
||||
|
||||
final Iterator<RangeHighlighter> iter = hls.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RangeHighlighter highlighter = iter.next();
|
||||
if (!highlighter.isValid() || (highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
|
||||
iter.remove();
|
||||
editor.getMarkupModel().removeHighlighter(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
|
||||
logger.debug("hls=" + hls);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
|
||||
logger.debug("hls=" + hls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
}
|
||||
|
||||
private fun buildJump(place: PlaceInfo): Jump? {
|
||||
val editor = injector.editorGroup.localEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val editor = injector.editorGroup.getEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val offset = place.caretPosition?.startOffset ?: return null
|
||||
|
||||
val bufferPosition = editor.offsetToBufferPosition(offset)
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.codeWithMe.ClientId
|
||||
import com.intellij.ide.bookmark.Bookmark
|
||||
import com.intellij.ide.bookmark.BookmarkGroup
|
||||
import com.intellij.ide.bookmark.BookmarksListener
|
||||
@ -18,7 +19,7 @@ import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
@ -28,11 +29,11 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.asSafely
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimEditorGroup
|
||||
import com.maddyhome.idea.vim.api.VimMarkService
|
||||
import com.maddyhome.idea.vim.api.VimMarkServiceBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.group.SystemMarks.Companion.createOrGetSystemMark
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.mark.IntellijMark
|
||||
import com.maddyhome.idea.vim.mark.Mark
|
||||
import com.maddyhome.idea.vim.mark.VimMark.Companion.create
|
||||
@ -193,6 +194,10 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
* This event indicates that a document is about to be changed. We use this event to update all the
|
||||
* editor's marks if text is about to be deleted.
|
||||
*
|
||||
* Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
|
||||
* which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
|
||||
* stored marks.
|
||||
*
|
||||
* @param event The change event
|
||||
*/
|
||||
override fun beforeDocumentChange(event: DocumentEvent) {
|
||||
@ -200,15 +205,18 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event")
|
||||
if (event.oldLength == 0) return
|
||||
val doc = event.document
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromDelete(IjVimEditor(anEditor), event.offset, event.oldLength)
|
||||
val anEditor = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromDelete(anEditor, event.offset, event.oldLength)
|
||||
}
|
||||
|
||||
/**
|
||||
* This event indicates that a document was just changed. We use this event to update all the editor's
|
||||
* marks if text was just added.
|
||||
*
|
||||
* Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
|
||||
* which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
|
||||
* stored marks.
|
||||
*
|
||||
* @param event The change event
|
||||
*/
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
@ -216,19 +224,19 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event")
|
||||
if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return
|
||||
val doc = event.document
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromInsert(IjVimEditor(anEditor), event.offset, event.newLength)
|
||||
val anEditor = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromInsert(anEditor, event.offset, event.newLength)
|
||||
}
|
||||
|
||||
private fun getAnEditor(doc: Document): Editor? {
|
||||
val editors = localEditors(doc)
|
||||
return if (editors.size > 0) {
|
||||
editors[0]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get any editor for the given document
|
||||
*
|
||||
* We need an editor to help calculate offsets for marks, and it doesn't matter which one we use, because they would
|
||||
* all return the same results. However, we cannot use [VimEditorGroup.getEditors] because the change might have
|
||||
* come from a remote guest and there might not be an open local editor.
|
||||
*/
|
||||
private fun getAnyEditorForDocument(doc: Document) =
|
||||
EditorFactory.getInstance().getEditors(doc).firstOrNull()?.let { IjVimEditor(it) }
|
||||
}
|
||||
|
||||
class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
|
||||
|
@ -8,9 +8,11 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
import org.jdom.Element
|
||||
|
||||
@Service
|
||||
internal class XMLGroup {
|
||||
/**
|
||||
* Set the text of an XML element, safely encode it if needed.
|
||||
|
@ -14,6 +14,7 @@ import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.PasteProvider
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
@ -51,6 +52,7 @@ import com.maddyhome.idea.vim.state.mode.isChar
|
||||
import com.maddyhome.idea.vim.state.mode.isLine
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
|
||||
@Service
|
||||
internal class PutGroup : VimPutBase() {
|
||||
|
||||
override fun getProviderForPasteViaIde(
|
||||
|
@ -20,9 +20,8 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
||||
import com.maddyhome.idea.vim.helper.inInsertMode
|
||||
import com.maddyhome.idea.vim.helper.inNormalMode
|
||||
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
||||
import com.maddyhome.idea.vim.helper.isTemplateActive
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.helper.vimDisabled
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.OptionConstants
|
||||
@ -30,7 +29,6 @@ import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inNormalMode
|
||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
|
||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
|
||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
|
||||
@ -55,9 +53,7 @@ internal object IdeaSelectionControl {
|
||||
selectionSource: VimListenerManager.SelectionSource = VimListenerManager.SelectionSource.OTHER,
|
||||
) {
|
||||
VimVisualTimer.singleTask(editor.vim.mode) { initialMode ->
|
||||
|
||||
if (VimPlugin.isNotEnabled()) return@singleTask
|
||||
if (editor.isIdeaVimDisabledHere) return@singleTask
|
||||
if (vimDisabled(editor)) return@singleTask
|
||||
|
||||
logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode")
|
||||
|
||||
@ -79,7 +75,7 @@ internal object IdeaSelectionControl {
|
||||
|
||||
logger.debug("Some carets have selection. State before adjustment: ${editor.vim.mode}")
|
||||
|
||||
editor.vim.vimStateMachine.mode = Mode.NORMAL()
|
||||
editor.vim.mode = Mode.NORMAL()
|
||||
|
||||
activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
|
||||
} else {
|
||||
|
@ -12,13 +12,13 @@ import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||
|
||||
internal fun moveCaretOneCharLeftFromSelectionEnd(editor: Editor, predictedMode: Mode) {
|
||||
if (predictedMode !is Mode.VISUAL) {
|
||||
|
@ -13,10 +13,10 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.command.engine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
/**
|
||||
* @author Alex Plate
|
||||
|
@ -27,7 +27,6 @@ import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.removeUserData
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.key
|
||||
import com.maddyhome.idea.vim.group.IjOptionConstants
|
||||
@ -39,7 +38,6 @@ import com.maddyhome.idea.vim.newapi.actionStartedFromVim
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
@ -340,7 +338,8 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
|
||||
override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
||||
val enterKey = key(key)
|
||||
val context = injector.executionContextManager.onEditor(editor.vim, dataContext?.vim)
|
||||
KeyHandler.getInstance().handleKey(editor.vim, enterKey, context)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
|
||||
}
|
||||
|
||||
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
|
||||
@ -362,4 +361,4 @@ internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
|
||||
}
|
||||
|
||||
internal val enableOctopus: Boolean
|
||||
get() = injector.globalOptions().octopushandler
|
||||
get() = injector.application.isOctopusEnabled()
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.CaretVisualAttributes
|
||||
@ -18,14 +19,17 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.IsReplaceCharListener
|
||||
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
|
||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorMode
|
||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorOptionHelper
|
||||
import com.maddyhome.idea.vim.options.helpers.GuiCursorType
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.awt.Color
|
||||
|
||||
@ -85,7 +89,10 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
|
||||
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
|
||||
|
||||
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
||||
(this as? EditorEx)?.setCaretVisible(true)
|
||||
// NOTE: At the moment, this causes project leak in tests
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
(this as? EditorEx)?.setCaretVisible(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
|
||||
@ -134,3 +141,31 @@ private object AttributesCache {
|
||||
|
||||
@TestOnly
|
||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
||||
|
||||
public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
|
||||
override fun isReplaceCharChanged(editor: VimEditor) {
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
|
||||
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
|
||||
private fun updateCaretsVisual(editor: VimEditor) {
|
||||
if (injector.globalOptions().ideaglobalmode) {
|
||||
updateAllEditorsCaretsVisual()
|
||||
} else {
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
ijEditor.updateCaretsVisualAttributes()
|
||||
ijEditor.updateCaretsVisualPosition()
|
||||
}
|
||||
}
|
||||
|
||||
public fun updateAllEditorsCaretsVisual() {
|
||||
injector.editorGroup.getEditors().forEach { editor ->
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
ijEditor.updateCaretsVisualAttributes()
|
||||
ijEditor.updateCaretsVisualPosition()
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
import com.maddyhome.idea.vim.options.OptionConstants
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
|
||||
internal val Mode.hasVisualSelection
|
||||
get() = when (this) {
|
||||
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.maddyhome.idea.vim.EventFacade
|
||||
import com.maddyhome.idea.vim.group.SearchGroup
|
||||
import com.maddyhome.idea.vim.group.VimMarkServiceImpl
|
||||
|
||||
internal object DocumentManager {
|
||||
private val docListeners = mutableSetOf<DocumentListener>()
|
||||
private val LISTENER_MARKER = Key<String>("VimlistenerMarker")
|
||||
|
||||
init {
|
||||
docListeners += VimMarkServiceImpl.MarkUpdater
|
||||
docListeners += SearchGroup.DocumentSearchListener.INSTANCE
|
||||
}
|
||||
|
||||
fun addListeners(doc: Document) {
|
||||
val marker = doc.getUserData(LISTENER_MARKER)
|
||||
if (marker != null) return
|
||||
|
||||
doc.putUserData(LISTENER_MARKER, "foo")
|
||||
for (docListener in docListeners) {
|
||||
EventFacade.getInstance().addDocumentListener(doc, docListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeListeners(doc: Document) {
|
||||
doc.getUserData(LISTENER_MARKER) ?: return
|
||||
|
||||
doc.putUserData(LISTENER_MARKER, null)
|
||||
for (docListener in docListeners) {
|
||||
EventFacade.getInstance().removeDocumentListener(doc, docListener)
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.common.IndentConfig;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import kotlin.Pair;
|
||||
@ -29,6 +30,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static java.lang.Integer.max;
|
||||
import static java.lang.Integer.min;
|
||||
|
||||
@ -197,7 +199,7 @@ public class EditorHelper {
|
||||
* @param file The virtual file get the editor for
|
||||
* @return The matching editor or null if no match was found
|
||||
*/
|
||||
public static @Nullable Editor getEditor(final @Nullable VirtualFile file) {
|
||||
public static @Nullable VimEditor getEditor(final @Nullable VirtualFile file) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
@ -206,12 +208,7 @@ public class EditorHelper {
|
||||
if (doc == null) {
|
||||
return null;
|
||||
}
|
||||
final List<Editor> editors = HelperKt.localEditors(doc);
|
||||
if (editors.size() > 0) {
|
||||
return editors.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static @NotNull String pad(final @NotNull Editor editor,
|
||||
|
@ -17,6 +17,7 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.util.ui.table.JBTableRowEditor
|
||||
import com.maddyhome.idea.vim.api.StringListOptionValue
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.group.IjOptionConstants
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
@ -37,25 +38,27 @@ public val Editor.fileSize: Int
|
||||
internal val Editor.isIdeaVimDisabledHere: Boolean
|
||||
get() {
|
||||
val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
|
||||
return disabledInDialog ||
|
||||
(!ClientId.isCurrentlyUnderLocalId) || // CWM-927
|
||||
(!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isDatabaseCell()) ||
|
||||
(!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isOneLineMode)
|
||||
return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
|
||||
!ClientId.isCurrentlyUnderLocalId || // CWM-927
|
||||
(ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine())
|
||||
}
|
||||
|
||||
private fun Editor.isDatabaseCell(): Boolean {
|
||||
return isTableCellEditor(this.component)
|
||||
private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean {
|
||||
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||
&& !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
|
||||
}
|
||||
|
||||
private val Editor.disabledInDialog: Boolean
|
||||
get() {
|
||||
val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
|
||||
return (
|
||||
!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog) &&
|
||||
!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
|
||||
) &&
|
||||
(!this.isPrimaryEditor() && !EditorHelper.isFileEditor(this))
|
||||
}
|
||||
private fun ideaVimDisabledForSingleLine(ideaVimSupportValue: StringListOptionValue): Boolean {
|
||||
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline)
|
||||
}
|
||||
|
||||
private fun Editor.isInDialog(): Boolean {
|
||||
return !this.isPrimaryEditor() && !EditorHelper.isFileEditor(this)
|
||||
}
|
||||
|
||||
private fun Editor.isSingleLine(): Boolean {
|
||||
return isTableCellEditor(this.component) || isOneLineMode
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the editor is a primary editor in the main editing area.
|
||||
|
@ -9,19 +9,12 @@
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.codeWithMe.ClientId
|
||||
import com.intellij.injected.editor.EditorWindow
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.ClientEditorManager
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||
import java.util.stream.Collectors
|
||||
|
||||
internal fun <T : Comparable<T>> sort(a: T, b: T) = if (a > b) b to a else a to b
|
||||
|
||||
@ -36,34 +29,6 @@ internal inline fun Editor.vimForEachCaret(action: (caret: Caret) -> Unit) {
|
||||
|
||||
internal fun Editor.getTopLevelEditor() = if (this is EditorWindow) this.delegate else this
|
||||
|
||||
/**
|
||||
* Return list of editors for local host (for code with me plugin)
|
||||
*/
|
||||
public fun localEditors(): List<Editor> {
|
||||
return ClientEditorManager.getCurrentInstance().editors().collect(Collectors.toList())
|
||||
}
|
||||
|
||||
public fun localEditors(doc: Document): List<Editor> {
|
||||
return EditorFactory.getInstance().getEditors(doc)
|
||||
.filter { editor -> editor.editorClientId.let { it == null || it == ClientId.currentOrNull } }
|
||||
}
|
||||
|
||||
public fun localEditors(doc: Document, project: Project): List<Editor> {
|
||||
return EditorFactory.getInstance().getEditors(doc, project)
|
||||
.filter { editor -> editor.editorClientId.let { it == null || it == ClientId.currentOrNull } }
|
||||
}
|
||||
|
||||
private val Editor.editorClientId: ClientId?
|
||||
get() {
|
||||
if (editorClientKey == null) {
|
||||
@Suppress("DEPRECATION")
|
||||
editorClientKey = Key.findKeyByName("editorClientIdby userData()") ?: return null
|
||||
}
|
||||
return editorClientKey?.let { this.getUserData(it) as? ClientId }
|
||||
}
|
||||
|
||||
private var editorClientKey: Key<*>? = null
|
||||
|
||||
@Suppress("IncorrectParentDisposable")
|
||||
internal fun Editor.isTemplateActive(): Boolean {
|
||||
val project = this.project ?: return false
|
||||
|
@ -90,7 +90,8 @@ internal class IjActionExecutor : VimActionExecutor {
|
||||
// because rider use async update method. See VIM-1819.
|
||||
// This method executes inside of lastUpdateAndCheckDumb
|
||||
// Another related issue: VIM-2604
|
||||
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
|
||||
ijAction.beforeActionPerformedUpdate(event)
|
||||
if (!event.presentation.isEnabled) return false
|
||||
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
|
||||
// Some ActionGroups should not be performed, but shown as a popup
|
||||
val popup = JBPopupFactory.getInstance()
|
||||
|
@ -34,15 +34,15 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
|
||||
val returnTo = this.vim.vimStateMachine.mode.returnTo
|
||||
when (returnTo) {
|
||||
ReturnTo.INSERT -> {
|
||||
this.vim.vimStateMachine.mode = Mode.INSERT
|
||||
this.vim.mode = Mode.INSERT
|
||||
}
|
||||
|
||||
ReturnTo.REPLACE -> {
|
||||
this.vim.vimStateMachine.mode = Mode.REPLACE
|
||||
this.vim.mode = Mode.REPLACE
|
||||
}
|
||||
|
||||
null -> {
|
||||
this.vim.vimStateMachine.mode = Mode.NORMAL()
|
||||
this.vim.mode = Mode.NORMAL()
|
||||
}
|
||||
}
|
||||
SelectionVimListenerSuppressor.lock().use {
|
||||
@ -67,15 +67,15 @@ internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
|
||||
val returnTo = this.vimStateMachine.mode.returnTo
|
||||
when (returnTo) {
|
||||
ReturnTo.INSERT -> {
|
||||
this.vimStateMachine.mode = Mode.INSERT
|
||||
this.mode = Mode.INSERT
|
||||
}
|
||||
|
||||
ReturnTo.REPLACE -> {
|
||||
this.vimStateMachine.mode = Mode.REPLACE
|
||||
this.mode = Mode.REPLACE
|
||||
}
|
||||
|
||||
null -> {
|
||||
this.vimStateMachine.mode = Mode.NORMAL()
|
||||
this.mode = Mode.NORMAL()
|
||||
}
|
||||
}
|
||||
SelectionVimListenerSuppressor.lock().use {
|
||||
|
@ -26,15 +26,13 @@ import com.intellij.spellchecker.SpellCheckerSeveritiesProvider;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.EngineEditorHelperKt;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.regexp.*;
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
|
||||
import com.maddyhome.idea.vim.common.CharacterPosition;
|
||||
import com.maddyhome.idea.vim.common.Direction;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.regexp.CharPointer;
|
||||
import com.maddyhome.idea.vim.regexp.RegExp;
|
||||
import com.maddyhome.idea.vim.regexp.*;
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparator;
|
||||
@ -1613,11 +1611,17 @@ public class SearchHelper {
|
||||
}
|
||||
else {
|
||||
IntIterator offsetIterator = offsets.iterator();
|
||||
offsetIterator.skip(skipCount);
|
||||
skip(offsetIterator, skipCount);
|
||||
return offsetIterator.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
private static void skip(IntIterator iterator, final int n) {
|
||||
if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n);
|
||||
int i = n;
|
||||
while (i-- != 0 && iterator.hasNext()) iterator.nextInt();
|
||||
}
|
||||
|
||||
private static @NotNull String parseMatchPairsOption(final VimEditor vimEditor) {
|
||||
List<String> pairs = options(injector, vimEditor).getMatchpairs();
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.options
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import org.jetbrains.annotations.Contract
|
||||
@ -86,11 +87,24 @@ private fun updateSearchHighlights(
|
||||
): Int {
|
||||
var currentMatchOffset = -1
|
||||
val projectManager = ProjectManager.getInstanceIfCreated() ?: return currentMatchOffset
|
||||
|
||||
// TODO: This implementation needs rethinking
|
||||
// It's a bit weird that we update search highlights across all open projects. It would make more sense to treat top
|
||||
// level project frame windows as separate applications, but we can't do this because IdeaVim does not maintain state
|
||||
// per-project.
|
||||
// So, to be clear, this will loop over each project, and therefore, for each project top-level frame, will update
|
||||
// search highlights in all editors for the document of the currently selected editor. It does not update highlights
|
||||
// for editors for the document that are in other projects.
|
||||
|
||||
for (project in projectManager.openProjects) {
|
||||
val current = FileEditorManager.getInstance(project).selectedTextEditor ?: continue
|
||||
// [VERSION UPDATE] 202+ Use editors
|
||||
val editors = localEditors(current.document, project)
|
||||
for (editor in editors) {
|
||||
val editors = injector.editorGroup.getEditors(IjVimDocument(current.document))
|
||||
for (vimEditor in editors) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
if (editor.project != project) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed.
|
||||
// Force update for the situations where the text is the same, but the ignore case values have changed.
|
||||
// E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
|
||||
|
@ -31,4 +31,8 @@ public object StringHelper {
|
||||
return Arrays.stream(string).flatMap { o: String -> injector.parser.parseKeys(o).stream() }
|
||||
.collect(Collectors.toList())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use key.isCloseKeyStroke()", ReplaceWith("key.isCloseKeyStroke()"))
|
||||
public fun isCloseKeyStroke(key: KeyStroke): Boolean = key.isCloseKeyStroke()
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ import com.intellij.openapi.util.UserDataHolder
|
||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
|
||||
import com.maddyhome.idea.vim.api.LocalMarkStorage
|
||||
import com.maddyhome.idea.vim.api.SelectionInfo
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.group.visual.VisualChange
|
||||
import com.maddyhome.idea.vim.group.visual.vimLeadSelectionOffset
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
@ -99,6 +99,8 @@ internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretTo
|
||||
internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
|
||||
internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()
|
||||
|
||||
internal var Editor.vimInitialised: Boolean by userDataOr { false }
|
||||
|
||||
// ------------------ Editor
|
||||
internal fun unInitializeEditor(editor: Editor) {
|
||||
editor.vimLastSelectionType = null
|
||||
@ -106,6 +108,7 @@ internal fun unInitializeEditor(editor: Editor) {
|
||||
editor.vimMorePanel = null
|
||||
editor.vimExOutput = null
|
||||
editor.vimLastHighlighters = null
|
||||
editor.vimInitialised = false
|
||||
}
|
||||
|
||||
internal var Editor.vimLastSearch: String? by userData()
|
||||
|
@ -21,6 +21,6 @@ public final class VimIcons {
|
||||
public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg");
|
||||
|
||||
private static @NotNull Icon load(@NotNull @NonNls String path) {
|
||||
return IconManager.getInstance().getIcon(path, VimIcons.class);
|
||||
return IconManager.getInstance().getIcon(path, VimIcons.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,19 @@
|
||||
package com.maddyhome.idea.vim.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.util.PsiEditorUtil
|
||||
import com.maddyhome.idea.vim.extension.ExtensionBeanClass
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.vimscript.model.commands.SetCommand
|
||||
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser
|
||||
|
||||
internal class UsePlugSyntaxInspection : LocalInspectionTool() {
|
||||
override fun getGroupDisplayName(): String {
|
||||
@ -23,11 +31,54 @@ internal class UsePlugSyntaxInspection : LocalInspectionTool() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
val file = holder.file
|
||||
if (file.name != ".ideavimrc" && file.name != "_ideavimrc") return PsiElementVisitor.EMPTY_VISITOR
|
||||
val plugins = buildPlugins()
|
||||
return object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (element !is LeafPsiElement) return
|
||||
holder.registerProblem(element, TextRange.create(10, 20), "Hi there")
|
||||
val myScript = VimscriptParser.parse(element.text)
|
||||
myScript.units.forEach { unit ->
|
||||
if (unit is SetCommand) {
|
||||
val argument = unit.argument
|
||||
val alias = plugins[argument]
|
||||
if (alias != null) {
|
||||
holder.registerProblem(
|
||||
element,
|
||||
unit.rangeInScript.let { TextRange(it.startOffset, it.endOffset - 1) },
|
||||
"""
|
||||
Use `Plug` syntax for defining extensions
|
||||
""".trimIndent(),
|
||||
object : LocalQuickFix {
|
||||
override fun getFamilyName(): String {
|
||||
return "Use Plug syntax"
|
||||
}
|
||||
|
||||
override fun applyFix(p0: Project, p1: ProblemDescriptor) {
|
||||
val editor = PsiEditorUtil.findEditor(file)
|
||||
editor?.document?.replaceString(
|
||||
unit.rangeInScript.startOffset,
|
||||
unit.rangeInScript.endOffset - 1,
|
||||
"Plug '$alias'"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPlugins(): HashMap<String, String> {
|
||||
val res = HashMap<String, String>()
|
||||
VimExtension.EP_NAME.extensions.forEach { extension: ExtensionBeanClass ->
|
||||
val alias = extension.aliases?.first { it.name?.count { it == '/' } == 1 }?.name
|
||||
?: extension.aliases?.firstOrNull()?.name
|
||||
val name = extension.name
|
||||
if (alias != null && name != null) {
|
||||
res[name] = alias
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.listener
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.acejump.session.SessionManager
|
||||
|
||||
@ -16,12 +16,11 @@ import org.acejump.session.SessionManager
|
||||
* Key handling for IdeaVim should be updated to editorHandler usage. In this case this class can be safely removed.
|
||||
*/
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
internal interface AceJumpService {
|
||||
fun isActive(editor: Editor): Boolean
|
||||
|
||||
companion object {
|
||||
fun getInstance(): AceJumpService? = ServiceManager.getService(AceJumpService::class.java)
|
||||
fun getInstance(): AceJumpService? = ApplicationManager.getApplication().getService(AceJumpService::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ internal object IdeaSpecifics {
|
||||
}
|
||||
}
|
||||
|
||||
if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) {
|
||||
if (hostEditor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
|
||||
val lookup = LookupManager.getActiveLookup(hostEditor)
|
||||
if (lookup != null) {
|
||||
val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
|
||||
@ -99,7 +99,7 @@ internal object IdeaSpecifics {
|
||||
|
||||
val editor = editor
|
||||
if (editor != null) {
|
||||
if (action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
|
||||
if (action is ChooseItemAction && injector.registerGroup.isRecording) {
|
||||
val prevDocumentLength = completionPrevDocumentLength
|
||||
val prevDocumentOffset = completionPrevDocumentOffset
|
||||
|
||||
@ -126,10 +126,12 @@ internal object IdeaSpecifics {
|
||||
)
|
||||
}
|
||||
) {
|
||||
val commandState = editor.vim.vimStateMachine
|
||||
commandState.mode = Mode.NORMAL()
|
||||
VimPlugin.getChange().insertBeforeCursor(editor.vim, event.dataContext.vim)
|
||||
KeyHandler.getInstance().reset(editor.vim)
|
||||
editor?.let {
|
||||
val commandState = it.vim.vimStateMachine
|
||||
it.vim.mode = Mode.NORMAL()
|
||||
VimPlugin.getChange().insertBeforeCursor(it.vim, event.dataContext.vim)
|
||||
KeyHandler.getInstance().reset(it.vim)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.listener
|
||||
|
||||
import com.intellij.codeWithMe.ClientId
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
@ -20,6 +21,8 @@ import com.intellij.openapi.editor.EditorKind
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction
|
||||
import com.intellij.openapi.editor.event.CaretEvent
|
||||
import com.intellij.openapi.editor.event.CaretListener
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent
|
||||
@ -49,6 +52,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.ExceptionUtil
|
||||
import com.maddyhome.idea.vim.EventFacade
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.KeyHandlerStateResetter
|
||||
import com.maddyhome.idea.vim.VimKeyListener
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.VimTypedActionHandler
|
||||
@ -62,15 +66,17 @@ import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.group.EditorGroup
|
||||
import com.maddyhome.idea.vim.group.FileGroup
|
||||
import com.maddyhome.idea.vim.group.IjOptions
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.OptionGroup
|
||||
import com.maddyhome.idea.vim.group.ScrollGroup
|
||||
import com.maddyhome.idea.vim.group.SearchGroup
|
||||
import com.maddyhome.idea.vim.group.VimMarkServiceImpl
|
||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer
|
||||
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
|
||||
import com.maddyhome.idea.vim.handler.correctorRequester
|
||||
import com.maddyhome.idea.vim.handler.keyCheckRequests
|
||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesListener
|
||||
import com.maddyhome.idea.vim.helper.GuicursorChangeListener
|
||||
import com.maddyhome.idea.vim.helper.StrictMode
|
||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||
@ -78,25 +84,23 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.forceBarCursor
|
||||
import com.maddyhome.idea.vim.helper.inVisualMode
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
import com.maddyhome.idea.vim.helper.resetVimLastColumn
|
||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
||||
import com.maddyhome.idea.vim.helper.vimDisabled
|
||||
import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipEvents
|
||||
import com.maddyhome.idea.vim.listener.MouseEventsDataHolder.skipNDragEvents
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
|
||||
import com.maddyhome.idea.vim.ui.ShowCmdWidgetUpdater
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
|
||||
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
|
||||
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
|
||||
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
|
||||
import com.maddyhome.idea.vim.vimDisposable
|
||||
import java.awt.event.MouseAdapter
|
||||
@ -128,6 +132,7 @@ private fun getOpeningEditor(newEditor: Editor) = newEditor.project?.let { proje
|
||||
internal object VimListenerManager {
|
||||
|
||||
private val logger = Logger.getInstance(VimListenerManager::class.java)
|
||||
private val editorListenersDisposableKey = Key.create<Disposable>("IdeaVim listeners disposable")
|
||||
private var firstEditorInitialised = false
|
||||
|
||||
fun turnOn() {
|
||||
@ -135,11 +140,30 @@ internal object VimListenerManager {
|
||||
EditorListeners.addAll()
|
||||
check(correctorRequester.tryEmit(Unit))
|
||||
check(keyCheckRequests.tryEmit(Unit))
|
||||
|
||||
val caretVisualAttributesListener = CaretVisualAttributesListener()
|
||||
injector.listenersNotifier.modeChangeListeners.add(caretVisualAttributesListener)
|
||||
injector.listenersNotifier.isReplaceCharListeners.add(caretVisualAttributesListener)
|
||||
caretVisualAttributesListener.updateAllEditorsCaretsVisual()
|
||||
|
||||
val modeWidgetListener = ModeWidgetListener()
|
||||
injector.listenersNotifier.modeChangeListeners.add(modeWidgetListener)
|
||||
injector.listenersNotifier.myEditorListeners.add(modeWidgetListener)
|
||||
injector.listenersNotifier.vimPluginListeners.add(modeWidgetListener)
|
||||
|
||||
val macroWidgetListener = MacroWidgetListener()
|
||||
injector.listenersNotifier.macroRecordingListeners.add(macroWidgetListener)
|
||||
injector.listenersNotifier.vimPluginListeners.add(macroWidgetListener)
|
||||
|
||||
injector.listenersNotifier.myEditorListeners.add(KeyHandlerStateResetter())
|
||||
injector.listenersNotifier.myEditorListeners.add(ShowCmdWidgetUpdater())
|
||||
}
|
||||
|
||||
fun turnOff() {
|
||||
GlobalListeners.disable()
|
||||
EditorListeners.removeAll()
|
||||
injector.listenersNotifier.reset()
|
||||
|
||||
check(correctorRequester.tryEmit(Unit))
|
||||
}
|
||||
|
||||
@ -157,23 +181,29 @@ internal object VimListenerManager {
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.number, EditorGroup.NumberChangeListener.INSTANCE)
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
|
||||
optionGroup.addGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
|
||||
|
||||
// This code is executed after ideavimrc execution, so we trigger onGlobalOptionChanged just in case
|
||||
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(Options.showmode, modeWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
|
||||
modeWidgetOptionListener.onGlobalOptionChanged()
|
||||
macroWidgetOptionListener.onGlobalOptionChanged()
|
||||
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
|
||||
|
||||
// Listen for and initialise new editors
|
||||
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable)
|
||||
val busConnection = ApplicationManager.getApplication().messageBus.connect(VimPlugin.getInstance().onOffDisposable)
|
||||
busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener)
|
||||
|
||||
EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable)
|
||||
val eventMulticaster = EditorFactory.getInstance().eventMulticaster as? EditorEventMulticasterEx
|
||||
eventMulticaster?.addFocusChangeListener(VimFocusListener, VimPlugin.getInstance().onOffDisposable)
|
||||
// Listen for focus change to update various features such as mode widget
|
||||
val eventMulticaster = EditorFactory.getInstance().eventMulticaster
|
||||
(eventMulticaster as? EditorEventMulticasterEx)?.addFocusChangeListener(
|
||||
VimFocusListener,
|
||||
VimPlugin.getInstance().onOffDisposable
|
||||
)
|
||||
|
||||
// Listen for document changes to update document state such as marks
|
||||
eventMulticaster.addDocumentListener(VimDocumentListener, VimPlugin.getInstance().onOffDisposable)
|
||||
}
|
||||
|
||||
fun disable() {
|
||||
@ -184,8 +214,8 @@ internal object VimListenerManager {
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showmode, modeWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
|
||||
}
|
||||
}
|
||||
@ -214,7 +244,9 @@ internal object VimListenerManager {
|
||||
|
||||
// We could have a split window in this list, but since they're all being initialised from the same opening editor
|
||||
// there's no need to use the SPLIT scenario
|
||||
localEditors().forEach { editor ->
|
||||
// Make sure we get all editors, including uninitialised
|
||||
injector.editorGroup.getEditorsRaw().forEach { vimEditor ->
|
||||
val editor = vimEditor.ij
|
||||
if (!initialisedEditors.contains(editor)) {
|
||||
add(editor, getOpeningEditor(editor)?.vim ?: injector.fallbackWindow, LocalOptionInitialisationScenario.NEW)
|
||||
}
|
||||
@ -222,18 +254,26 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
localEditors().forEach { editor ->
|
||||
remove(editor, false)
|
||||
injector.editorGroup.getEditors().forEach { editor ->
|
||||
remove(editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
fun add(editor: Editor, openingEditor: VimEditor, scenario: LocalOptionInitialisationScenario) {
|
||||
// We shouldn't be called with anything other than local editors, but let's just be sure. This will prevent any
|
||||
// unsupported editor from incorrectly being initialised.
|
||||
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
|
||||
if (vimDisabled(editor)) return
|
||||
|
||||
// As I understand, there is no need to pass a disposable that also disposes on editor close
|
||||
// because all editor resources will be garbage collected anyway on editor close
|
||||
// Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need
|
||||
// to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each
|
||||
// editor's disposable individually.
|
||||
val disposable = editor.project?.vimDisposable ?: return
|
||||
|
||||
val listenersDisposable = Disposer.newDisposable(disposable)
|
||||
editor.putUserData(editorListenersDisposable, listenersDisposable)
|
||||
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
||||
|
||||
Disposer.register(listenersDisposable) {
|
||||
if (VimListenerTestObject.enabled) {
|
||||
@ -256,54 +296,69 @@ internal object VimListenerManager {
|
||||
eventFacade.addCaretListener(editor, EditorCaretHandler, listenersDisposable)
|
||||
|
||||
VimPlugin.getEditor().editorCreated(editor)
|
||||
|
||||
VimPlugin.getChange().editorCreated(editor, listenersDisposable)
|
||||
|
||||
injector.listenersNotifier.notifyEditorCreated(vimEditor)
|
||||
|
||||
Disposer.register(listenersDisposable) {
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
|
||||
VimPlugin.getEditor().editorDeinit(editor, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(editor: Editor, isReleased: Boolean) {
|
||||
val editorDisposable = editor.getUserData(editorListenersDisposable)
|
||||
fun remove(editor: Editor) {
|
||||
val editorDisposable = editor.removeUserData(editorListenersDisposableKey)
|
||||
if (editorDisposable != null) {
|
||||
Disposer.dispose(editorDisposable)
|
||||
}
|
||||
else StrictMode.fail("Editor doesn't have disposable attached. $editor")
|
||||
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
|
||||
else {
|
||||
// We definitely do not expect this to happen
|
||||
StrictMode.fail("Editor doesn't have disposable attached. $editor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies other IdeaVim components of focus gain/loss, e.g. the mode widget. This will be called with non-local Code
|
||||
* With Me editors.
|
||||
*/
|
||||
private object VimFocusListener : FocusChangeListener {
|
||||
override fun focusGained(editor: Editor) {
|
||||
if (vimDisabled(editor)) return
|
||||
injector.listenersNotifier.notifyEditorFocusGained(editor.vim)
|
||||
}
|
||||
|
||||
override fun focusLost(editor: Editor) {
|
||||
if (vimDisabled(editor)) return
|
||||
injector.listenersNotifier.notifyEditorFocusLost(editor.vim)
|
||||
}
|
||||
}
|
||||
|
||||
val editorListenersDisposable = Key.create<Disposable>("IdeaVim listeners disposable")
|
||||
|
||||
object VimCaretListener : CaretListener {
|
||||
override fun caretAdded(event: CaretEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
/**
|
||||
* Notifies other IdeaVim components of document changes. This will be called for all documents, even those only
|
||||
* open in non-local Code With Me guest editors, which we still want to process (e.g. to update marks when a guest
|
||||
* edits a file. Updating search highlights will be a no-op if there are no open local editors)
|
||||
*/
|
||||
private object VimDocumentListener : DocumentListener {
|
||||
override fun beforeDocumentChange(event: DocumentEvent) {
|
||||
VimMarkServiceImpl.MarkUpdater.beforeDocumentChange(event)
|
||||
SearchGroup.DocumentSearchListener.INSTANCE.beforeDocumentChange(event)
|
||||
}
|
||||
|
||||
override fun caretRemoved(event: CaretEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
VimMarkServiceImpl.MarkUpdater.documentChanged(event)
|
||||
SearchGroup.DocumentSearchListener.INSTANCE.documentChanged(event)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the selected file editor changes. In other words, when the user selects a new tab. Used to remember the
|
||||
* last selected file, update search highlights in the new tab, etc. This will be called with non-local Code With Me
|
||||
* guest editors.
|
||||
*/
|
||||
class VimFileEditorManagerListener : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
if (VimPlugin.isNotEnabled()) return
|
||||
// We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly
|
||||
if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return
|
||||
|
||||
val newEditor = event.newEditor
|
||||
if (newEditor is TextEditor) {
|
||||
@ -321,6 +376,10 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to editor creation events in order to initialise IdeaVim compatible editors. This listener is called for all
|
||||
* editors, including non-local hidden Code With Me editors.
|
||||
*/
|
||||
private object VimEditorFactoryListener : EditorFactoryListener, FileOpenedSyncListener {
|
||||
private data class OpeningEditor(
|
||||
val editor: Editor,
|
||||
@ -332,6 +391,8 @@ internal object VimListenerManager {
|
||||
private val openingEditorKey: Key<OpeningEditor> = Key("IdeaVim::OpeningEditor")
|
||||
|
||||
override fun editorCreated(event: EditorFactoryEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
|
||||
// This callback is called when an editor is created, but we cannot completely rely on it to initialise options.
|
||||
// We can find the currently selected editor, which we can use as the opening editor, and we're given the new
|
||||
// editor, but we don't know enough about it - this function is called before the new editor is added to an
|
||||
@ -352,7 +413,7 @@ internal object VimListenerManager {
|
||||
openingEditor == null -> LocalOptionInitialisationScenario.EDIT
|
||||
else -> LocalOptionInitialisationScenario.NEW
|
||||
}
|
||||
add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
|
||||
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
|
||||
firstEditorInitialised = true
|
||||
}
|
||||
else {
|
||||
@ -381,6 +442,7 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun editorReleased(event: EditorFactoryEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
val vimEditor = event.editor.vim
|
||||
injector.listenersNotifier.notifyEditorReleased(vimEditor)
|
||||
injector.markService.editorReleased(vimEditor)
|
||||
@ -404,6 +466,8 @@ internal object VimListenerManager {
|
||||
// editor is modified
|
||||
editorsWithProviders.forEach {
|
||||
(it.fileEditor as? TextEditor)?.editor?.let { editor ->
|
||||
if (vimDisabled(editor)) return@let
|
||||
|
||||
val openingEditor = editor.removeUserData(openingEditorKey)
|
||||
val owningEditorWindow = getOwningEditorWindow(editor)
|
||||
val isInSameSplit = owningEditorWindow == openingEditor?.owningEditorWindow
|
||||
@ -424,7 +488,7 @@ internal object VimListenerManager {
|
||||
(openingEditor.canBeReused || openingEditor.isPreview) && isInSameSplit && openingEditorIsClosed -> LocalOptionInitialisationScenario.EDIT
|
||||
else -> LocalOptionInitialisationScenario.NEW
|
||||
}
|
||||
add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
|
||||
EditorListeners.add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
|
||||
firstEditorInitialised = true
|
||||
}
|
||||
}
|
||||
@ -439,12 +503,16 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for when an editor's text selection changes. Only registered for editors that we're interested in (so only
|
||||
* local editors). Fixes incorrect mouse selection at end of line, and synchronises selections across other editors.
|
||||
*/
|
||||
private object EditorSelectionHandler : SelectionListener {
|
||||
/**
|
||||
* This event is executed for each caret using [com.intellij.openapi.editor.CaretModel.runForEachCaret]
|
||||
*/
|
||||
override fun selectionChanged(selectionEvent: SelectionEvent) {
|
||||
if (selectionEvent.editor.isIdeaVimDisabledHere) return
|
||||
VimVisualTimer.drop()
|
||||
val editor = selectionEvent.editor
|
||||
val document = editor.document
|
||||
@ -464,7 +532,9 @@ internal object VimListenerManager {
|
||||
val startOffset = selectionEvent.newRange.startOffset
|
||||
val endOffset = selectionEvent.newRange.endOffset
|
||||
|
||||
if (skipNDragEvents < skipEvents && lineStart != lineEnd && startOffset == caretOffset) {
|
||||
// TODO: It is very confusing that this logic is split between EditorSelectionHandler and EditorMouseHandler
|
||||
if (MouseEventsDataHolder.dragEventCount < MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
&& lineStart != lineEnd && startOffset == caretOffset) {
|
||||
if (lineEnd == endOffset - 1) {
|
||||
// When starting on an empty line and dragging vertically upwards onto
|
||||
// another line, the selection should include the entirety of the empty line
|
||||
@ -494,13 +564,24 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for mouse events registered with editors that we are interested (so only local editors). Responsible for:
|
||||
* * Hiding ex entry and output panels when clicking inside editor area (but not when right-clicking)
|
||||
* * Removing secondary carets on mouse click (such as visual block selection)
|
||||
* * Exiting visual or select mode on mouse click
|
||||
* * Resets the dragEventCount on mouse press + release
|
||||
* * Fix up Vim selected mode on mouse release, after dragging
|
||||
* * Force bar cursor while dragging, which looks better because IntelliJ selects a character once selection has got
|
||||
* over halfway through the char, while Vim selects when it enters the character bounding box
|
||||
*
|
||||
* @see ComponentMouseListener
|
||||
*/
|
||||
// TODO: Can we merge this with ComponentMouseListener to fully handle all mouse actions in one place?
|
||||
private object EditorMouseHandler : EditorMouseListener, EditorMouseMotionListener {
|
||||
private var mouseDragging = false
|
||||
private var cutOffFixed = false
|
||||
|
||||
override fun mouseDragged(e: EditorMouseEvent) {
|
||||
if (e.editor.isIdeaVimDisabledHere) return
|
||||
|
||||
val caret = e.editor.caretModel.primaryCaret
|
||||
|
||||
clearFirstSelectionEvents(e)
|
||||
@ -552,7 +633,7 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
skipNDragEvents -= 1
|
||||
MouseEventsDataHolder.dragEventCount -= 1
|
||||
}
|
||||
|
||||
/**
|
||||
@ -567,7 +648,7 @@ internal object VimListenerManager {
|
||||
* (Both with mouse and with v$. IdeaVim treats v$ as an exclusive selection)
|
||||
*/
|
||||
private fun clearFirstSelectionEvents(e: EditorMouseEvent) {
|
||||
if (skipNDragEvents > 0) {
|
||||
if (MouseEventsDataHolder.dragEventCount > 0) {
|
||||
logger.debug("Mouse dragging")
|
||||
VimVisualTimer.swingTimer?.stop()
|
||||
if (!mouseDragging) {
|
||||
@ -593,9 +674,7 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun mousePressed(event: EditorMouseEvent) {
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
|
||||
skipNDragEvents = skipEvents
|
||||
MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
SelectionVimListenerSuppressor.reset()
|
||||
}
|
||||
|
||||
@ -606,12 +685,10 @@ internal object VimListenerManager {
|
||||
* - Click-hold and switch editor (ctrl-tab)
|
||||
*/
|
||||
override fun mouseReleased(event: EditorMouseEvent) {
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
|
||||
SelectionVimListenerSuppressor.unlock()
|
||||
|
||||
clearFirstSelectionEvents(event)
|
||||
skipNDragEvents = skipEvents
|
||||
MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
if (mouseDragging) {
|
||||
logger.debug("Release mouse after dragging")
|
||||
val editor = event.editor
|
||||
@ -631,7 +708,6 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun mouseClicked(event: EditorMouseEvent) {
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
logger.debug("Mouse clicked")
|
||||
|
||||
if (event.area == EditorMouseEventArea.EDITING_AREA) {
|
||||
@ -677,13 +753,21 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mouse listener registered to the editor component for editors that we are interested in (so only local editors).
|
||||
* Fixes some issues with mouse selection, namely:
|
||||
* * Clicking at the end of the line will place the caret on the last character rather than after it
|
||||
* * Double-clicking a word will place the caret on the last character rather than after it
|
||||
*
|
||||
* @see EditorMouseHandler
|
||||
*/
|
||||
// TODO: Can we merge this with ComponentMouseListener to fully handle all mouse actions in one place?
|
||||
private object ComponentMouseListener : MouseAdapter() {
|
||||
|
||||
var cutOffEnd = false
|
||||
|
||||
override fun mousePressed(e: MouseEvent?) {
|
||||
val editor = (e?.component as? EditorComponentImpl)?.editor ?: return
|
||||
if (editor.isIdeaVimDisabledHere) return
|
||||
val predictedMode = IdeaSelectionControl.predictMode(editor, SelectionSource.MOUSE)
|
||||
when (e.clickCount) {
|
||||
1 -> {
|
||||
@ -720,10 +804,22 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caret listener registered only for editors that we're interested in. Used to update caret shapes when carets are
|
||||
* added/removed. Also tracks the expected last column location of the caret.
|
||||
*/
|
||||
private object EditorCaretHandler : CaretListener {
|
||||
override fun caretPositionChanged(event: CaretEvent) {
|
||||
event.caret?.resetVimLastColumn()
|
||||
}
|
||||
|
||||
override fun caretAdded(event: CaretEvent) {
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
}
|
||||
|
||||
override fun caretRemoved(event: CaretEvent) {
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
}
|
||||
}
|
||||
|
||||
enum class SelectionSource {
|
||||
@ -738,6 +834,6 @@ internal object VimListenerTestObject {
|
||||
}
|
||||
|
||||
private object MouseEventsDataHolder {
|
||||
const val skipEvents = 3
|
||||
var skipNDragEvents = skipEvents
|
||||
const val allowedSkippedDragEvents = 3
|
||||
var dragEventCount = allowedSkippedDragEvents
|
||||
}
|
||||
|
@ -43,6 +43,10 @@ internal class IjVimApplication : VimApplicationBase() {
|
||||
return ApplicationManager.getApplication().isUnitTestMode
|
||||
}
|
||||
|
||||
override fun isInternal(): Boolean {
|
||||
return ApplicationManager.getApplication().isInternal
|
||||
}
|
||||
|
||||
override fun postKey(stroke: KeyStroke, editor: VimEditor) {
|
||||
val component: Component = SwingUtilities.getAncestorOfClass(Window::class.java, editor.ij.component)
|
||||
val event = createKeyEvent(stroke, component)
|
||||
@ -54,10 +58,6 @@ internal class IjVimApplication : VimApplicationBase() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun localEditors(): List<VimEditor> {
|
||||
return com.maddyhome.idea.vim.helper.localEditors().map { IjVimEditor(it) }
|
||||
}
|
||||
|
||||
override fun runWriteCommand(editor: VimEditor, name: String?, groupId: Any?, command: Runnable) {
|
||||
RunnableHelper.runWriteCommand((editor as IjVimEditor).editor.project, command, name, groupId)
|
||||
}
|
||||
@ -82,6 +82,12 @@ internal class IjVimApplication : VimApplicationBase() {
|
||||
com.maddyhome.idea.vim.helper.runAfterGotFocus(runnable)
|
||||
}
|
||||
|
||||
override fun isOctopusEnabled(): Boolean {
|
||||
val property = System.getProperty("octopus.handler") ?: "true"
|
||||
if (property.isBlank()) return true
|
||||
return property.toBoolean()
|
||||
}
|
||||
|
||||
private fun createKeyEvent(stroke: KeyStroke, component: Component): KeyEvent {
|
||||
return KeyEvent(
|
||||
component,
|
||||
|
@ -11,7 +11,6 @@ package com.maddyhome.idea.vim.newapi
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.VisualPosition
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.maddyhome.idea.vim.api.BufferPosition
|
||||
import com.maddyhome.idea.vim.api.CaretRegisterStorage
|
||||
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
|
||||
@ -42,14 +41,6 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
||||
|
||||
init {
|
||||
if (caret.isValid) {
|
||||
Disposer.register(caret) {
|
||||
(registerStorage as CaretRegisterStorageBase).clearListener()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val registerStorage: CaretRegisterStorage
|
||||
get() {
|
||||
var storage = this.caret.registerStorage
|
||||
|
@ -51,8 +51,6 @@ import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.helper.getTopLevelEditor
|
||||
import com.maddyhome.idea.vim.helper.inExMode
|
||||
import com.maddyhome.idea.vim.helper.isTemplateActive
|
||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualPosition
|
||||
import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode
|
||||
import com.maddyhome.idea.vim.helper.vimLastSelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
@ -243,14 +241,6 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateCaretsVisualAttributes() {
|
||||
editor.updateCaretsVisualAttributes()
|
||||
}
|
||||
|
||||
override fun updateCaretsVisualPosition() {
|
||||
editor.updateCaretsVisualPosition()
|
||||
}
|
||||
|
||||
override fun offsetToVisualPosition(offset: Int): VimVisualPosition {
|
||||
return editor.offsetToVisualPosition(offset).let { VimVisualPosition(it.line, it.column, it.leansRight) }
|
||||
}
|
||||
|
@ -80,11 +80,11 @@ import com.maddyhome.idea.vim.helper.UndoRedoHelper
|
||||
import com.maddyhome.idea.vim.helper.VimCommandLineHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.history.VimHistory
|
||||
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
|
||||
import com.maddyhome.idea.vim.macro.VimMacro
|
||||
import com.maddyhome.idea.vim.put.VimPut
|
||||
import com.maddyhome.idea.vim.register.VimRegisterGroup
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
|
||||
import com.maddyhome.idea.vim.ui.VimRcFileState
|
||||
import com.maddyhome.idea.vim.undo.VimUndoRedo
|
||||
import com.maddyhome.idea.vim.vimscript.Executor
|
||||
@ -207,7 +207,7 @@ internal class IjVimInjector : VimInjectorBase() {
|
||||
override fun commandStateFor(editor: VimEditor): VimStateMachine {
|
||||
var res = editor.ij.vimStateMachine
|
||||
if (res == null) {
|
||||
res = VimStateMachineImpl(editor)
|
||||
res = VimStateMachineImpl()
|
||||
editor.ij.vimStateMachine = res
|
||||
}
|
||||
return res
|
||||
|
@ -14,17 +14,7 @@ import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
|
||||
|
||||
internal class ActionTracker : CounterUsagesCollector() {
|
||||
companion object {
|
||||
private val GROUP = EventLogGroup("vim.actions", 1, "FUS")
|
||||
private val TRACKED_ACTIONS = GROUP.registerEvent(
|
||||
"tracked",
|
||||
EventFields.StringValidatedByCustomRule("action_id", ActionRuleValidator::class.java),
|
||||
)
|
||||
private val COPIED_ACTIONS = GROUP.registerEvent(
|
||||
"copied",
|
||||
EventFields.StringValidatedByCustomRule("action_id", ActionRuleValidator::class.java),
|
||||
)
|
||||
|
||||
object Util {
|
||||
fun logTrackedAction(actionId: String) {
|
||||
TRACKED_ACTIONS.log(actionId)
|
||||
}
|
||||
@ -36,3 +26,14 @@ internal class ActionTracker : CounterUsagesCollector() {
|
||||
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
}
|
||||
|
||||
private val GROUP = EventLogGroup("vim.actions", 1)
|
||||
private val TRACKED_ACTIONS = GROUP.registerEvent(
|
||||
"tracked",
|
||||
EventFields.StringValidatedByCustomRule("action_id", ActionRuleValidator::class.java),
|
||||
)
|
||||
private val COPIED_ACTIONS = GROUP.registerEvent(
|
||||
"copied",
|
||||
EventFields.StringValidatedByCustomRule("action_id", ActionRuleValidator::class.java),
|
||||
)
|
||||
|
||||
|
@ -47,28 +47,28 @@ internal class OptionsState : ApplicationUsagesCollector() {
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GROUP = EventLogGroup("vim.options", 1, "FUS")
|
||||
|
||||
private val IDEAJOIN = BooleanEventField(IjOptions.ideajoin.name)
|
||||
private val IDEAMARKS = BooleanEventField(IjOptions.ideamarks.name)
|
||||
private val IDEAREFACTOR = EventFields.String(IjOptions.idearefactormode.name, IjOptionConstants.ideaRefactorModeValues.toList())
|
||||
private val IDEAPUT = BooleanEventField(OptionConstants.clipboard_ideaput)
|
||||
private val IDEASTATUSICON = EventFields.String(IjOptions.ideastatusicon.name, IjOptionConstants.ideaStatusIconValues.toList())
|
||||
private val IDEAWRITE = EventFields.String(IjOptions.ideawrite.name, IjOptionConstants.ideaWriteValues.toList())
|
||||
private val IDEASELECTION = BooleanEventField(OptionConstants.selectmode_ideaselection)
|
||||
private val IDEAVIMSUPPORT = EventFields.StringList(IjOptions.ideavimsupport.name, IjOptionConstants.ideavimsupportValues.toList())
|
||||
|
||||
private val OPTIONS: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.options",
|
||||
IDEAJOIN,
|
||||
IDEAMARKS,
|
||||
IDEAREFACTOR,
|
||||
IDEAPUT,
|
||||
IDEASTATUSICON,
|
||||
IDEAWRITE,
|
||||
IDEASELECTION,
|
||||
IDEAVIMSUPPORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val GROUP = EventLogGroup("vim.options", 1)
|
||||
private val IDEAJOIN = BooleanEventField(IjOptions.ideajoin.name)
|
||||
private val IDEAMARKS = BooleanEventField(IjOptions.ideamarks.name)
|
||||
private val IDEAREFACTOR =
|
||||
EventFields.String(IjOptions.idearefactormode.name, IjOptionConstants.ideaRefactorModeValues.toList())
|
||||
private val IDEAPUT = BooleanEventField(OptionConstants.clipboard_ideaput)
|
||||
private val IDEASTATUSICON =
|
||||
EventFields.String(IjOptions.ideastatusicon.name, IjOptionConstants.ideaStatusIconValues.toList())
|
||||
private val IDEAWRITE = EventFields.String(IjOptions.ideawrite.name, IjOptionConstants.ideaWriteValues.toList())
|
||||
private val IDEASELECTION = BooleanEventField(OptionConstants.selectmode_ideaselection)
|
||||
private val IDEAVIMSUPPORT =
|
||||
EventFields.StringList(IjOptions.ideavimsupport.name, IjOptionConstants.ideavimsupportValues.toList())
|
||||
private val OPTIONS: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.options",
|
||||
IDEAJOIN,
|
||||
IDEAMARKS,
|
||||
IDEAREFACTOR,
|
||||
IDEAPUT,
|
||||
IDEASTATUSICON,
|
||||
IDEAWRITE,
|
||||
IDEASELECTION,
|
||||
IDEAVIMSUPPORT,
|
||||
)
|
@ -14,6 +14,8 @@ import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.VarargEventId
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.statistic.PluginState.Util.enabledExtensions
|
||||
import com.maddyhome.idea.vim.statistic.PluginState.Util.extensionNames
|
||||
import com.maddyhome.idea.vim.ui.JoinEap
|
||||
|
||||
internal class PluginState : ApplicationUsagesCollector() {
|
||||
@ -30,21 +32,32 @@ internal class PluginState : ApplicationUsagesCollector() {
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GROUP = EventLogGroup("vim.common", 1, "FUS")
|
||||
|
||||
val extensionNames = listOf("textobj-entire", "argtextobj", "ReplaceWithRegister", "vim-paragraph-motion", "highlightedyank", "multiple-cursors", "exchange", "NERDTree", "surround", "commentary", "matchit", "textobj-indent")
|
||||
val enabledExtensions = HashSet<String>()
|
||||
|
||||
private val PLUGIN_ENABLED = EventFields.Boolean("is_plugin_enabled")
|
||||
private val IS_EAP = EventFields.Boolean("is_EAP_active")
|
||||
private val ENABLED_EXTENSIONS = EventFields.StringList("enabled_extensions", extensionNames)
|
||||
|
||||
private val PLUGIN_STATE: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.common",
|
||||
PLUGIN_ENABLED,
|
||||
IS_EAP,
|
||||
ENABLED_EXTENSIONS,
|
||||
object Util {
|
||||
internal val extensionNames = listOf(
|
||||
"textobj-entire",
|
||||
"argtextobj",
|
||||
"ReplaceWithRegister",
|
||||
"vim-paragraph-motion",
|
||||
"highlightedyank",
|
||||
"multiple-cursors",
|
||||
"exchange",
|
||||
"NERDTree",
|
||||
"surround",
|
||||
"commentary",
|
||||
"matchit",
|
||||
"textobj-indent"
|
||||
)
|
||||
internal val enabledExtensions = HashSet<String>()
|
||||
}
|
||||
}
|
||||
|
||||
private val GROUP = EventLogGroup("vim.common", 1)
|
||||
private val PLUGIN_ENABLED = EventFields.Boolean("is_plugin_enabled")
|
||||
private val IS_EAP = EventFields.Boolean("is_EAP_active")
|
||||
private val ENABLED_EXTENSIONS = EventFields.StringList("enabled_extensions", extensionNames)
|
||||
private val PLUGIN_STATE: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.common",
|
||||
PLUGIN_ENABLED,
|
||||
IS_EAP,
|
||||
ENABLED_EXTENSIONS,
|
||||
)
|
@ -70,95 +70,6 @@ internal class ShortcutConflictState : ApplicationUsagesCollector() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GROUP = EventLogGroup("vim.handlers", 1, "FUS")
|
||||
|
||||
private val keyStrokes = listOf(
|
||||
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
)
|
||||
private val KEY_STROKE = EventFields.String("key_stroke", keyStrokes.map { it.toReadableString() })
|
||||
private val HANDLER_MODE = EventFields.Enum<HandledModes>("handler")
|
||||
private val HANDLER = GROUP.registerEvent("vim.handler", KEY_STROKE, HANDLER_MODE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KeyStroke.toReadableString(): String {
|
||||
@ -187,3 +98,89 @@ private enum class HandledModes {
|
||||
VISUAL_AND_SELECT_IDE,
|
||||
VISUAL_AND_SELECT_VIM,
|
||||
}
|
||||
|
||||
private val GROUP = EventLogGroup("vim.handlers", 1)
|
||||
private val keyStrokes = listOf(
|
||||
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('1'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('2'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('3'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('4'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('5'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('6'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('7'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('8'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('9'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('0'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK),
|
||||
|
||||
KeyStroke.getKeyStroke('A'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('B'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('C'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('D'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('E'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('F'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('G'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('H'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('I'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('J'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('K'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('L'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('M'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('N'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('O'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('P'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Q'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('R'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('S'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('T'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('U'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('V'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('W'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('X'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Y'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('Z'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke('['.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
KeyStroke.getKeyStroke(']'.code, CTRL_DOWN_MASK + SHIFT_DOWN_MASK),
|
||||
)
|
||||
private val KEY_STROKE = EventFields.String("key_stroke", keyStrokes.map { it.toReadableString() })
|
||||
private val HANDLER_MODE = EventFields.Enum<HandledModes>("handler")
|
||||
private val HANDLER = GROUP.registerEvent("vim.handler", KEY_STROKE, HANDLER_MODE)
|
@ -13,6 +13,7 @@ import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.VarargEventId
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.maddyhome.idea.vim.statistic.PluginState.Util.extensionNames
|
||||
import com.maddyhome.idea.vim.vimscript.services.VimRcService
|
||||
|
||||
internal class VimscriptState : ApplicationUsagesCollector() {
|
||||
@ -22,56 +23,56 @@ internal class VimscriptState : ApplicationUsagesCollector() {
|
||||
override fun getMetrics(): Set<MetricEvent> {
|
||||
return setOf(
|
||||
VIMSCRIPT.metric(
|
||||
SOURCED_FILES with sourcedFiles.size,
|
||||
SOURCED_FILES with Util.sourcedFiles.size,
|
||||
IDEAVIMRC_SIZE with (VimRcService.findIdeaVimRc()?.readLines()?.filter { !it.matches(Regex("\\s*\".*")) && it.isNotBlank() }?.size ?: -1),
|
||||
EXTENSIONS_ENABLED_BY_SET with (PluginState.enabledExtensions - extensionsEnabledWithPlug).toList(),
|
||||
EXTENSIONS_ENABLED_BY_PLUG with extensionsEnabledWithPlug.toList(),
|
||||
IS_IDE_SPECIFIC_CONFIGURATION_USED with isIDESpecificConfigurationUsed,
|
||||
IS_LOOP_USED with isLoopUsed,
|
||||
IS_IF_USED with isIfUsed,
|
||||
IS_MAP_EXPR_USED with isMapExprUsed,
|
||||
IS_FUNCTION_DEF_USED with isFunctionDeclarationUsed,
|
||||
IS_FUNCTION_CALL_USED with isFunctionCallUsed,
|
||||
EXTENSIONS_ENABLED_BY_SET with (PluginState.Util.enabledExtensions - Util.extensionsEnabledWithPlug).toList(),
|
||||
EXTENSIONS_ENABLED_BY_PLUG with Util.extensionsEnabledWithPlug.toList(),
|
||||
IS_IDE_SPECIFIC_CONFIGURATION_USED with Util.isIDESpecificConfigurationUsed,
|
||||
IS_LOOP_USED with Util.isLoopUsed,
|
||||
IS_IF_USED with Util.isIfUsed,
|
||||
IS_MAP_EXPR_USED with Util.isMapExprUsed,
|
||||
IS_FUNCTION_DEF_USED with Util.isFunctionDeclarationUsed,
|
||||
IS_FUNCTION_CALL_USED with Util.isFunctionCallUsed,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val GROUP = EventLogGroup("vim.vimscript", 1, "FUS")
|
||||
|
||||
object Util {
|
||||
val sourcedFiles = HashSet<String>()
|
||||
val extensionsEnabledWithPlug = HashSet<String>()
|
||||
var isIDESpecificConfigurationUsed = false
|
||||
|
||||
var isIDESpecificConfigurationUsed = false
|
||||
var isLoopUsed = false
|
||||
var isIfUsed = false
|
||||
var isMapExprUsed = false
|
||||
var isFunctionDeclarationUsed = false
|
||||
var isFunctionCallUsed = false
|
||||
|
||||
private val SOURCED_FILES = EventFields.RoundedInt("number_of_sourced_files")
|
||||
private val IDEAVIMRC_SIZE = EventFields.RoundedInt("ideavimrc_size")
|
||||
private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", PluginState.extensionNames)
|
||||
private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", PluginState.extensionNames)
|
||||
private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used")
|
||||
private val IS_LOOP_USED = EventFields.Boolean("is_loop_used")
|
||||
private val IS_IF_USED = EventFields.Boolean("is_if_used")
|
||||
private val IS_MAP_EXPR_USED = EventFields.Boolean("is_map_expr_used")
|
||||
private val IS_FUNCTION_DEF_USED = EventFields.Boolean("is_function_declaration_used")
|
||||
private val IS_FUNCTION_CALL_USED = EventFields.Boolean("is_function_call_used")
|
||||
|
||||
private val VIMSCRIPT: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.vimscript",
|
||||
SOURCED_FILES,
|
||||
IDEAVIMRC_SIZE,
|
||||
EXTENSIONS_ENABLED_BY_SET,
|
||||
EXTENSIONS_ENABLED_BY_PLUG,
|
||||
IS_IDE_SPECIFIC_CONFIGURATION_USED,
|
||||
IS_LOOP_USED,
|
||||
IS_IF_USED,
|
||||
IS_MAP_EXPR_USED,
|
||||
IS_FUNCTION_DEF_USED,
|
||||
IS_FUNCTION_CALL_USED,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val GROUP = EventLogGroup("vim.vimscript", 1)
|
||||
private val SOURCED_FILES = EventFields.RoundedInt("number_of_sourced_files")
|
||||
private val IDEAVIMRC_SIZE = EventFields.RoundedInt("ideavimrc_size")
|
||||
private val EXTENSIONS_ENABLED_BY_SET = EventFields.StringList("extensions_enabled_by_set", extensionNames)
|
||||
private val EXTENSIONS_ENABLED_BY_PLUG = EventFields.StringList("extensions_enabled_by_plug", extensionNames)
|
||||
private val IS_IDE_SPECIFIC_CONFIGURATION_USED = EventFields.Boolean("is_IDE-specific_configuration_used")
|
||||
private val IS_LOOP_USED = EventFields.Boolean("is_loop_used")
|
||||
private val IS_IF_USED = EventFields.Boolean("is_if_used")
|
||||
private val IS_MAP_EXPR_USED = EventFields.Boolean("is_map_expr_used")
|
||||
private val IS_FUNCTION_DEF_USED = EventFields.Boolean("is_function_declaration_used")
|
||||
private val IS_FUNCTION_CALL_USED = EventFields.Boolean("is_function_call_used")
|
||||
|
||||
private val VIMSCRIPT: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.vimscript",
|
||||
SOURCED_FILES,
|
||||
IDEAVIMRC_SIZE,
|
||||
EXTENSIONS_ENABLED_BY_SET,
|
||||
EXTENSIONS_ENABLED_BY_PLUG,
|
||||
IS_IDE_SPECIFIC_CONFIGURATION_USED,
|
||||
IS_LOOP_USED,
|
||||
IS_IF_USED,
|
||||
IS_MAP_EXPR_USED,
|
||||
IS_FUNCTION_DEF_USED,
|
||||
IS_FUNCTION_CALL_USED,
|
||||
)
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.statistic
|
||||
|
||||
import com.intellij.internal.statistic.beans.MetricEvent
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.VarargEventId
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.ui.widgets.mode.ModeWidgetFactory
|
||||
|
||||
public class WidgetState : ApplicationUsagesCollector() {
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
|
||||
override fun getMetrics(): Set<MetricEvent> {
|
||||
return setOf(
|
||||
WIDGET.metric(
|
||||
IS_MODE_WIDGET_SHOWN with isWidgetShown(),
|
||||
MODE_WIDGET_THEME_LIGHT with getModeWidgetTheme("_light"),
|
||||
MODE_WIDGET_THEME_DARK with getModeWidgetTheme("_dark"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isWidgetShown(): Boolean {
|
||||
for (project in ProjectManager.getInstance().openProjects) {
|
||||
val statusBar = WindowManager.getInstance()?.getStatusBar(project) ?: continue
|
||||
val widgets = statusBar.allWidgets ?: continue
|
||||
if (widgets.any { it.ID() == ModeWidgetFactory.ID }) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getModeWidgetTheme(postfix: String): String {
|
||||
if (injector.variableService.getVimVariable("widget_mode_is_full_customization$postfix")?.asBoolean() == true) {
|
||||
return "ADVANCED CUSTOMIZATION"
|
||||
}
|
||||
val themeString = injector.variableService.getVimVariable("widget_mode_theme$postfix")?.asString()
|
||||
return if (themeString?.lowercase() == "colorless") {
|
||||
"COLORLESS"
|
||||
} else {
|
||||
"TERM"
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
private val GROUP = EventLogGroup("vim.widget", 1, "FUS")
|
||||
|
||||
private val IS_MODE_WIDGET_SHOWN = EventFields.Boolean("is-mode-widget-shown")
|
||||
private val MODE_WIDGET_THEME_LIGHT = EventFields.String("mode-widget-theme-light", listOf("TERM", "COLORLESS", "ADVANCED CUSTOMIZATION"))
|
||||
private val MODE_WIDGET_THEME_DARK = EventFields.String("mode-widget-theme-dark", listOf("TERM", "COLORLESS", "ADVANCED CUSTOMIZATION"))
|
||||
|
||||
private val WIDGET: VarargEventId = GROUP.registerVarargEvent(
|
||||
"vim.widget",
|
||||
IS_MODE_WIDGET_SHOWN,
|
||||
MODE_WIDGET_THEME_LIGHT,
|
||||
MODE_WIDGET_THEME_DARK,
|
||||
)
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ internal class Troubleshooter {
|
||||
|
||||
fun findIncorrectMappings(): List<Problem> {
|
||||
val problems = ArrayList<Problem>()
|
||||
MappingMode.values().forEach { mode ->
|
||||
MappingMode.entries.forEach { mode ->
|
||||
injector.keyGroup.getKeyMapping(mode).getByOwner(MappingOwner.IdeaVim.InitScript).forEach { (_, to) ->
|
||||
if (to is ToKeysMappingInfo) {
|
||||
if (":action" in to.toKeys.joinToString { it.keyChar.toString() }) {
|
||||
|
@ -18,8 +18,8 @@ import com.intellij.util.IJSwingUtilities;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.diagnostic.VimLogger;
|
||||
import com.maddyhome.idea.vim.helper.HelperKt;
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||
import com.maddyhome.idea.vim.helper.UiHelper;
|
||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||
@ -104,7 +104,7 @@ public class ExOutputPanel extends JPanel {
|
||||
}
|
||||
|
||||
private static int countLines(@NotNull String text) {
|
||||
if (text.length() == 0) {
|
||||
if (text.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -139,14 +139,14 @@ public class ExOutputPanel extends JPanel {
|
||||
}
|
||||
|
||||
public void setText(@NotNull @Nls(capitalization = Nls.Capitalization.Sentence) String data) {
|
||||
if (data.length() > 0 && data.charAt(data.length() - 1) == '\n') {
|
||||
if (!data.isEmpty() && data.charAt(data.length() - 1) == '\n') {
|
||||
data = data.substring(0, data.length() - 1);
|
||||
}
|
||||
|
||||
myText.setText(data);
|
||||
myText.setFont(UiHelper.selectFont(data));
|
||||
myText.setCaretPosition(0);
|
||||
if (data.length() > 0) {
|
||||
if (!data.isEmpty()) {
|
||||
activate();
|
||||
}
|
||||
}
|
||||
@ -366,8 +366,11 @@ public class ExOutputPanel extends JPanel {
|
||||
@Override
|
||||
public void lookAndFeelChanged(@NotNull LafManager source) {
|
||||
if (VimPlugin.isNotEnabled()) return;
|
||||
// Calls updateUI on this and child components
|
||||
for (Editor editor : HelperKt.localEditors()) {
|
||||
|
||||
// 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 vimEditor : injector.getEditorGroup().getEditors()) {
|
||||
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
if (!ExOutputPanel.isPanelActive(editor)) continue;
|
||||
IJSwingUtilities.updateComponentTreeUI(ExOutputPanel.getInstance(editor));
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.trace
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import java.awt.KeyEventDispatcher
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.awt.Toolkit
|
||||
@ -60,7 +60,7 @@ public object ModalEntry {
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
if (editor.vimStateMachine.isRecording) {
|
||||
if (injector.registerGroup.isRecording) {
|
||||
KeyHandler.getInstance().modalEntryKeys += stroke
|
||||
}
|
||||
if (!processor(stroke)) {
|
||||
|
@ -12,6 +12,7 @@ import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
@ -159,8 +160,8 @@ internal class ReloadVimRc : DumbAwareAction() {
|
||||
internal class ReloadFloatingToolbar : AbstractFloatingToolbarProvider(ACTION_GROUP) {
|
||||
override val autoHideable: Boolean = false
|
||||
|
||||
override fun register(component: FloatingToolbarComponent, parentDisposable: Disposable) {
|
||||
super.register(component, parentDisposable)
|
||||
override fun register(dataContext: DataContext, component: FloatingToolbarComponent, parentDisposable: Disposable) {
|
||||
super.register(dataContext, component, parentDisposable)
|
||||
val action = {
|
||||
component.scheduleShow()
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
package com.maddyhome.idea.vim.ui
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
@ -21,11 +20,14 @@ import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
|
||||
import com.intellij.util.Consumer
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.EditorListener
|
||||
import com.maddyhome.idea.vim.helper.EngineStringHelper
|
||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
|
||||
import org.jetbrains.annotations.NonNls
|
||||
@ -130,11 +132,18 @@ internal class Widget(project: Project) :
|
||||
override fun copy(): StatusBarWidget = Widget(project)
|
||||
}
|
||||
|
||||
internal class WidgetUpdater : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
// Update when changing selected editor
|
||||
internal class ShowCmdWidgetUpdater : EditorListener {
|
||||
override fun focusGained(editor: VimEditor) {
|
||||
editor.ij.project?.let { selectionChanged(it) }
|
||||
}
|
||||
|
||||
override fun focusLost(editor: VimEditor) {
|
||||
editor.ij.project?.let { selectionChanged(it) }
|
||||
}
|
||||
|
||||
private fun selectionChanged(project: Project) {
|
||||
val windowManager = WindowManager.getInstance()
|
||||
val statusBar = windowManager.getStatusBar(event.manager.project)
|
||||
val statusBar = windowManager.getStatusBar(project)
|
||||
statusBar.updateWidget(ShowCmd.ID)
|
||||
}
|
||||
}
|
||||
|
@ -9,15 +9,16 @@ package com.maddyhome.idea.vim.ui
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.ActionToolbarPosition
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
|
||||
import com.intellij.openapi.keymap.KeymapUtil
|
||||
import com.intellij.openapi.options.Configurable
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.ui.ComboBoxTableRenderer
|
||||
import com.intellij.openapi.ui.StripeTable
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.ui.DumbAwareActionButton
|
||||
import com.intellij.ui.HyperlinkLabel
|
||||
import com.intellij.ui.IdeBorderFactory
|
||||
import com.intellij.ui.JBColor
|
||||
@ -169,7 +170,7 @@ internal class VimEmulationConfigurable : Configurable {
|
||||
return getColumnModel().getColumn(column.index)
|
||||
}
|
||||
|
||||
private class ShortcutOwnerRenderer : ComboBoxTableRenderer<ShortcutOwner>(ShortcutOwner.values()) {
|
||||
private class ShortcutOwnerRenderer : ComboBoxTableRenderer<ShortcutOwner>(ShortcutOwner.entries.toTypedArray()) {
|
||||
override fun customizeComponent(owner: ShortcutOwner, table: JTable, isSelected: Boolean) {
|
||||
super.customizeComponent(owner, table, isSelected)
|
||||
if (owner == ShortcutOwner.UNDEFINED) {
|
||||
@ -192,7 +193,7 @@ internal class VimEmulationConfigurable : Configurable {
|
||||
private val ourMembers: MutableMap<Int, Column> = HashMap()
|
||||
|
||||
init {
|
||||
for (column in values()) {
|
||||
for (column in entries) {
|
||||
ourMembers[column.index] = column
|
||||
}
|
||||
}
|
||||
@ -224,7 +225,7 @@ internal class VimEmulationConfigurable : Configurable {
|
||||
}
|
||||
|
||||
override fun getColumnCount(): Int {
|
||||
return Column.values().size
|
||||
return Column.entries.size
|
||||
}
|
||||
|
||||
override fun getValueAt(rowIndex: Int, columnIndex: Int): Any? {
|
||||
@ -294,19 +295,21 @@ internal class VimEmulationConfigurable : Configurable {
|
||||
|
||||
private class CopyForRcAction(
|
||||
private val myModel: VimShortcutConflictsTable.Model,
|
||||
) : DumbAwareActionButton(
|
||||
) : DumbAwareAction(
|
||||
"Copy Config for .ideavimrc",
|
||||
"Copy config for .ideavimrc in sethandler format",
|
||||
AllIcons.Actions.Copy,
|
||||
) {
|
||||
|
||||
override fun updateButton(e: AnActionEvent) {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val enabled: Boolean = myModel.rows.stream().anyMatch {
|
||||
it.owner is AllModes && (it.owner as AllModes).owner != ShortcutOwner.UNDEFINED
|
||||
}
|
||||
e.presentation.isEnabled = enabled
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val stringBuilder = StringBuilder()
|
||||
for (row in myModel.rows) {
|
||||
@ -328,14 +331,16 @@ internal class VimEmulationConfigurable : Configurable {
|
||||
class ResetHandlersAction(
|
||||
private val myModel: VimShortcutConflictsTable.Model,
|
||||
private val myTable: VimShortcutConflictsTable,
|
||||
) : DumbAwareActionButton("Reset Handlers", "Reset handlers", AllIcons.General.Reset) {
|
||||
override fun updateButton(e: AnActionEvent) {
|
||||
) : DumbAwareAction("Reset Handlers", "Reset handlers", AllIcons.General.Reset) {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val enabled: Boolean = myModel.rows.stream().anyMatch {
|
||||
it.owner is AllModes && (it.owner as AllModes).owner != ShortcutOwner.UNDEFINED
|
||||
}
|
||||
e.presentation.isEnabled = enabled
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
TableUtil.stopEditing(myTable)
|
||||
for (row in myModel.rows) {
|
||||
|
@ -114,7 +114,8 @@ internal class CompleteEntryAction : TextAction(ExEditorKit.CompleteEntry) {
|
||||
// write action
|
||||
// * The key handler routines get the chance to clean up and reset state
|
||||
val entry = ExEntryPanel.getInstance().entry
|
||||
KeyHandler.getInstance().handleKey(entry.editor.vim, stroke, entry.context.vim)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(entry.editor.vim, stroke, entry.context.vim, keyHandler.keyHandlerState)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -125,10 +125,12 @@ internal object ExEditorKit : DefaultEditorKit() {
|
||||
if (target.useHandleKeyFromEx) {
|
||||
val entry = ExEntryPanel.getInstance().entry
|
||||
val editor = entry.editor
|
||||
KeyHandler.getInstance().handleKey(
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(
|
||||
editor.vim,
|
||||
key,
|
||||
injector.executionContextManager.onEditor(editor.vim, entry.context.vim),
|
||||
keyHandler.keyHandlerState,
|
||||
)
|
||||
} else {
|
||||
val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
|
||||
|
@ -36,10 +36,12 @@ internal class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : Dum
|
||||
val keyStroke = getKeyStroke(e)
|
||||
if (keyStroke != null) {
|
||||
val editor = exEntryPanel.entry.editor
|
||||
KeyHandler.getInstance().handleKey(
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(
|
||||
editor.vim,
|
||||
keyStroke,
|
||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
||||
keyHandler.keyHandlerState
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -121,16 +121,11 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
|
||||
void setType(@NotNull String type) {
|
||||
String hkey = null;
|
||||
switch (type.charAt(0)) {
|
||||
case '/':
|
||||
case '?':
|
||||
hkey = HistoryConstants.SEARCH;
|
||||
break;
|
||||
case ':':
|
||||
hkey = HistoryConstants.COMMAND;
|
||||
break;
|
||||
}
|
||||
String hkey = switch (type.charAt(0)) {
|
||||
case '/', '?' -> HistoryConstants.SEARCH;
|
||||
case ':' -> HistoryConstants.COMMAND;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
if (hkey != null) {
|
||||
history = VimPlugin.getHistory().getEntries(hkey, 0, 0);
|
||||
@ -140,7 +135,7 @@ public class ExTextField extends JTextField {
|
||||
|
||||
/**
|
||||
* Stores the current text for use in filtering history. Required for scrolling through multiple history entries
|
||||
*
|
||||
* <p>
|
||||
* Called whenever the text is changed, either by typing, or by special characters altering the text (e.g. Delete)
|
||||
*/
|
||||
void saveLastEntry() {
|
||||
@ -304,7 +299,10 @@ public class ExTextField extends JTextField {
|
||||
*/
|
||||
void cancel() {
|
||||
clearCurrentAction();
|
||||
VimPlugin.getProcess().cancelExEntry(new IjVimEditor(EditorHolderService.getInstance().getEditor()), true);
|
||||
Editor editor = EditorHolderService.getInstance().getEditor();
|
||||
if (editor != null) {
|
||||
VimPlugin.getProcess().cancelExEntry(new IjVimEditor(editor), true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentAction(@NotNull MultiStepAction action, char pendingIndicator) {
|
||||
@ -322,7 +320,7 @@ public class ExTextField extends JTextField {
|
||||
|
||||
/**
|
||||
* Text to show while composing a digraph or inserting a literal or register
|
||||
*
|
||||
* <p>
|
||||
* The prompt character is inserted directly into the text of the text field, rather than drawn over the top of the
|
||||
* current character. When the action has been completed, the new character(s) are either inserted or overwritten,
|
||||
* depending on the insert/overwrite status of the text field. This mimics Vim's behaviour.
|
||||
@ -491,7 +489,7 @@ public class ExTextField extends JTextField {
|
||||
|
||||
/**
|
||||
* Updates the bounds of the caret and repaints those bounds.
|
||||
*
|
||||
* <p>
|
||||
* This method is not guaranteed to be called before paint(). The bounds are for use by repaint().
|
||||
*
|
||||
* @param r The current location of the caret, usually provided by MapToView. The x and y appear to be the upper
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user