mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
21 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
a7def05aa8
|
|||
51e13a5f20
|
|||
9b67260d5a
|
|||
ac37432db6
|
|||
4c946568e4
|
|||
7e70eed1ab
|
|||
43ae90044b
|
|||
d79f7c23c5
|
|||
6033450158
|
|||
ea86d7132c
|
|||
711d1f0329
|
|||
338e137347
|
|||
2b0e9bfec5
|
|||
b42346b9e1
|
|||
d58d3ca8b0
|
|||
22b2ca2352
|
|||
1c98daa180
|
|||
de906bcbac
|
|||
05fac8bf00
|
|||
a985d260f7
|
|||
9f4c679d77
|
2
.github/workflows/mergePr.yml
vendored
2
.github/workflows/mergePr.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
if: false
|
||||
if: github.event.pull_request.merged == true && github.repository == 'JetBrains/ideavim'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
12
.github/workflows/runUiOctopusTests.yml
vendored
12
.github/workflows/runUiOctopusTests.yml
vendored
@@ -9,13 +9,20 @@ 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:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -23,7 +30,7 @@ jobs:
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
gradle runIdeForUiTests > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -45,7 +52,6 @@ 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
|
||||
|
7
.github/workflows/runUiPyTests.yml
vendored
7
.github/workflows/runUiPyTests.yml
vendored
@@ -18,7 +18,11 @@ jobs:
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -48,5 +52,4 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-py-tests/build/reports
|
||||
sandbox-idea-log
|
7
.github/workflows/runUiTests.yml
vendored
7
.github/workflows/runUiTests.yml
vendored
@@ -15,7 +15,11 @@ jobs:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -45,7 +49,6 @@ 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
|
||||
|
34
.github/workflows/updateAffectedRate.yml
vendored
Normal file
34
.github/workflows/updateAffectedRate.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
|
||||
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
|
||||
|
||||
# This workflow syncs changes from the docs folder of IdeaVim to the IdeaVim.wiki repository
|
||||
|
||||
name: Update Affected Rate field on YouTrack
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
|
||||
steps:
|
||||
- name: Fetch origin repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Update YouTrack
|
||||
run: ./gradlew scripts:updateAffectedRates
|
||||
env:
|
||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
5
.github/workflows/updateChangelog.yml
vendored
5
.github/workflows/updateChangelog.yml
vendored
@@ -7,12 +7,15 @@ 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: false
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
1
.teamcity/_Self/Project.kt
vendored
1
.teamcity/_Self/Project.kt
vendored
@@ -25,7 +25,6 @@ object Project : Project({
|
||||
// Active tests
|
||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
||||
buildType(TestingBuildType("2024.1", "<default>"))
|
||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
|
||||
buildType(PropertyBased)
|
||||
|
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,6 +63,7 @@ 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,24 +117,33 @@ 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,9 +1,11 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
@@ -11,18 +13,6 @@ 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,14 +495,6 @@ 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,19 +23,15 @@ 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.
|
||||
|
||||
## 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
|
||||
## To Be Released
|
||||
|
||||
### 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.23-1.0.20")
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
|
||||
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,6 +32,7 @@ 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
|
||||
@@ -48,14 +49,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.9.0.202403050737-r")
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
classpath("org.kohsuke:github-api:1.305")
|
||||
|
||||
classpath("io.ktor:ktor-client-core:2.3.10")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.10")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.10")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
|
||||
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")
|
||||
|
||||
// This comes from the changelog plugin
|
||||
// classpath("org.jetbrains:markdown:0.3.1")
|
||||
@@ -69,11 +70,11 @@ plugins {
|
||||
application
|
||||
id("java-test-fixtures")
|
||||
|
||||
id("org.jetbrains.intellij") version "1.17.3"
|
||||
id("org.jetbrains.intellij") version "1.17.1"
|
||||
id("org.jetbrains.changelog") version "2.2.0"
|
||||
|
||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||
id("com.dorongold.task-tree") version "3.0.0"
|
||||
id("com.dorongold.task-tree") version "2.1.1"
|
||||
|
||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
||||
}
|
||||
@@ -141,14 +142,14 @@ dependencies {
|
||||
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.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")
|
||||
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")
|
||||
}
|
||||
|
||||
configurations {
|
||||
@@ -206,11 +207,6 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +260,7 @@ tasks {
|
||||
runPluginVerifier {
|
||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
||||
teamCityOutputFormat.set(true)
|
||||
// ideVersions.set(listOf("IC-2021.3.4"))
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
@@ -308,14 +305,26 @@ tasks {
|
||||
from(createOpenApiSourceJar) { into("lib/src") }
|
||||
}
|
||||
|
||||
patchPluginXml {
|
||||
val pluginVersion = version
|
||||
// Don't forget to update plugin.xml
|
||||
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>"""
|
||||
)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
@@ -420,14 +429,12 @@ val prId: String by project
|
||||
|
||||
tasks.register("updateMergedPr") {
|
||||
doLast {
|
||||
val x = changelog.getUnreleased()
|
||||
println("x")
|
||||
// if (project.hasProperty("prId")) {
|
||||
// println("Got pr id: $prId")
|
||||
// updateMergedPr(prId.toInt())
|
||||
// } else {
|
||||
// error("Cannot get prId")
|
||||
// }
|
||||
if (project.hasProperty("prId")) {
|
||||
println("Got pr id: $prId")
|
||||
updateMergedPr(prId.toInt())
|
||||
} else {
|
||||
error("Cannot get prId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,13 +8,12 @@
|
||||
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||
ideaVersion=2024.1
|
||||
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-34
|
||||
version=chylex-28
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.22
|
||||
antlrVersion=4.10.1
|
||||
@@ -29,7 +28,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.6.2
|
||||
kotlinxSerializationVersion=1.5.1
|
||||
|
||||
slackUrl=
|
||||
youtrackToken=
|
||||
|
135390
qodana.sarif.json
135390
qodana.sarif.json
File diff suppressed because one or more lines are too long
@@ -21,9 +21,6 @@ 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.23")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
|
||||
|
||||
implementation("io.ktor:ktor-client-core:2.3.10")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.10")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.10")
|
||||
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("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.9.0.202403050737-r")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
implementation("com.vdurmont:semver4j:3.1.0")
|
||||
}
|
||||
|
||||
@@ -58,6 +58,13 @@ tasks.register("checkNewPluginDependencies", JavaExec::class) {
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
}
|
||||
|
||||
tasks.register("updateAffectedRates", JavaExec::class) {
|
||||
group = "verification"
|
||||
description = "This job updates Affected Rate field on YouTrack"
|
||||
mainClass.set("scripts.YouTrackUsersAffectedKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
}
|
||||
|
||||
tasks.register("calculateNewVersion", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.CalculateNewVersionKt")
|
||||
|
@@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||
*/
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val knownPlugins = setOf(
|
||||
val knownPlugins = listOf(
|
||||
"IdeaVimExtension",
|
||||
"github.zgqq.intellij-enhance",
|
||||
"org.jetbrains.IdeaVim-EasyMotion",
|
||||
@@ -31,12 +31,7 @@ val knownPlugins = setOf(
|
||||
"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
|
||||
@@ -47,15 +42,19 @@ suspend fun main() {
|
||||
parameter("dependency", "IdeaVIM")
|
||||
parameter("includeOptional", true)
|
||||
}
|
||||
val output = response.body<List<String>>().toSet()
|
||||
val output = response.body<List<String>>()
|
||||
println(output)
|
||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
if (newPlugins.isNotEmpty()) {
|
||||
// val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
if (knownPlugins != output) {
|
||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
error(
|
||||
"""
|
||||
|
||||
Unregistered plugins:
|
||||
${newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" }}
|
||||
${if (newPlugins.isNotEmpty()) newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No unregistered plugins"}
|
||||
|
||||
Removed plugins:
|
||||
${if (removedPlugins.isNotEmpty()) removedPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No removed plugins"}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
@@ -8,12 +8,6 @@
|
||||
|
||||
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]
|
||||
@@ -25,12 +19,10 @@ 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 != mergeBaseCommit }
|
||||
val logDiff = log.takeWhile { it.id.name != objectId.name }
|
||||
val numCommits = logDiff.size
|
||||
println("Log diff size is $numCommits")
|
||||
check(numCommits < 450) {
|
||||
@@ -43,18 +35,3 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
Normal file
62
scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 scripts
|
||||
|
||||
import io.ktor.client.call.*
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
val areaWeights = setOf(
|
||||
Triple("118-53212", "Plugins", 50),
|
||||
Triple("118-53220", "Vim Script", 30),
|
||||
Triple("118-54084", "Esc", 100),
|
||||
)
|
||||
|
||||
suspend fun updateRates() {
|
||||
println("Updating rates of the issues")
|
||||
areaWeights.forEach { (id, name, weight) ->
|
||||
val unmappedIssues = unmappedIssues(name)
|
||||
println("Got ${unmappedIssues.size} for $name area")
|
||||
|
||||
unmappedIssues.forEach { issueId ->
|
||||
print("Trying to update issue $issueId: ")
|
||||
val response = updateCustomField(issueId) {
|
||||
put("name", "Affected Rate")
|
||||
put("\$type", "SimpleIssueCustomField")
|
||||
put("value", weight)
|
||||
}
|
||||
|
||||
println(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unmappedIssues(area: String): List<String> {
|
||||
val areaProcessed = if (" " in area) "{$area}" else area
|
||||
val res = issuesQuery(
|
||||
query = "project: VIM Affected Rate: {No affected rate} Area: $areaProcessed #Unresolved",
|
||||
fields = "id,idReadable"
|
||||
)
|
||||
return res.body<JsonArray>().map { it.jsonObject }.map { it["idReadable"]!!.jsonPrimitive.content }
|
||||
}
|
||||
|
||||
suspend fun getAreasWithoutWeight(): Set<Pair<String, String>> {
|
||||
val allAreas = getAreaValues()
|
||||
return allAreas
|
||||
.filterNot { it.key in areaWeights.map { it.first }.toSet() }
|
||||
.entries
|
||||
.map { it.key to it.value }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
suspend fun main() {
|
||||
updateRates()
|
||||
}
|
@@ -11,6 +11,7 @@ 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;
|
||||
@@ -79,6 +80,14 @@ 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);
|
||||
}
|
||||
@@ -89,12 +98,20 @@ 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) {
|
||||
@@ -102,18 +119,30 @@ 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.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
|
||||
/**
|
||||
@@ -36,10 +36,8 @@ 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) {
|
||||
injector.editorGroup.getEditors().forEach { vimEditor ->
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
localEditors().forEach { 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()
|
||||
registerShortcutsWithoutActions()
|
||||
registerEmptyShortcuts() // todo most likely it is not needed
|
||||
}
|
||||
|
||||
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||
@@ -46,11 +46,12 @@ public object RegisterActions {
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
}
|
||||
|
||||
private fun registerShortcutsWithoutActions() {
|
||||
private fun registerEmptyShortcuts() {
|
||||
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,7 +353,6 @@ 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.Level.PROJECT)
|
||||
@Service
|
||||
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, context.vim, handler.keyHandlerState)
|
||||
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim))
|
||||
if (startTime != null) {
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
||||
|
@@ -44,6 +44,7 @@ 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
|
||||
@@ -78,8 +79,11 @@ 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
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
||||
KeyHandler.getInstance().handleKey(
|
||||
editor.vim,
|
||||
keyStroke,
|
||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
||||
)
|
||||
if (start != null) {
|
||||
val duration = System.currentTimeMillis() - start
|
||||
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
||||
@@ -376,10 +380,6 @@ private class ActionEnableStatus(
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ActionEnableStatus(isEnabled=$isEnabled, message='$message', logLevel=$logLevel)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<ActionEnableStatus>()
|
||||
|
||||
|
@@ -14,7 +14,6 @@ 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
|
||||
@@ -22,7 +21,6 @@ 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
|
||||
@@ -31,67 +29,21 @@ 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 func = injector.globalOptions().operatorfunc
|
||||
if (func.isEmpty()) {
|
||||
val operatorFunction = injector.keyGroup.operatorFunction
|
||||
if (operatorFunction == null) {
|
||||
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 arguments = listOf(SimpleExpression(arg))
|
||||
handler.executeFunction(arguments, editor, context, scriptContext)
|
||||
|
||||
val result = operatorFunction.apply(editor, context, selectionType)
|
||||
VimRepeater.repeatHandler = saveRepeatHandler
|
||||
return true
|
||||
return result
|
||||
}
|
||||
|
||||
@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
|
||||
|
@@ -34,7 +34,7 @@ public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecu
|
||||
}
|
||||
injector.editorGroup.notifyIdeaJoin(editor)
|
||||
var res = true
|
||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
||||
res = false
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExe
|
||||
return true
|
||||
}
|
||||
var res = true
|
||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
if (!caret.isValid) return@forEach
|
||||
val range = caretsAndSelections[caret] ?: return@forEach
|
||||
if (!injector.changeGroup.deleteJoinRange(
|
||||
|
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.Sin
|
||||
return true
|
||||
}
|
||||
var res = true
|
||||
editor.carets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
editor.carets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
if (!caret.isValid) return@forEach
|
||||
val range = caretsAndSelections[caret] ?: return@forEach
|
||||
if (!injector.changeGroup.deleteJoinRange(
|
||||
|
@@ -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(machine.mode)
|
||||
get() = machine.isOperatorPending
|
||||
|
||||
public val mode: Mode
|
||||
public val mode: CommandState.Mode
|
||||
get() {
|
||||
val myMode = machine.mode
|
||||
return when (myMode) {
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,8 @@
|
||||
package com.maddyhome.idea.vim.common
|
||||
|
||||
import com.intellij.application.options.CodeStyle
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
||||
@@ -37,12 +39,13 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(editor: Editor): IndentConfig {
|
||||
return create(editor, editor.project)
|
||||
fun create(editor: Editor, context: DataContext): IndentConfig {
|
||||
return create(editor, PlatformDataKeys.PROJECT.getData(context))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun create(editor: Editor, project: Project?): IndentConfig {
|
||||
@JvmOverloads
|
||||
fun create(editor: Editor, project: Project? = editor.project): IndentConfig {
|
||||
val indentOptions = if (project != null) {
|
||||
CodeStyle.getIndentOptions(project, editor.document)
|
||||
} else {
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.extension
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
@@ -15,34 +14,21 @@ 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
|
||||
|
||||
/**
|
||||
@@ -134,6 +120,12 @@ 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
|
||||
@@ -143,9 +135,8 @@ public object VimExtensionFacade {
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
||||
val context = injector.executionContextManager.onEditor(editor.vim)
|
||||
keys.forEach { KeyHandler.getInstance().handleKey(editor.vim, it, context, false, false) }
|
||||
}
|
||||
|
||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||
@@ -161,7 +152,7 @@ public object VimExtensionFacade {
|
||||
LOG.trace("Unit test mode is active")
|
||||
val mappingStack = KeyHandler.getInstance().keyStack
|
||||
mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
|
||||
if (injector.registerGroup.isRecording) {
|
||||
if (editor.vim.vimStateMachine.isRecording) {
|
||||
KeyHandler.getInstance().modalEntryKeys += it
|
||||
}
|
||||
}
|
||||
@@ -182,8 +173,8 @@ public object VimExtensionFacade {
|
||||
|
||||
/** Returns a string typed in the input box similar to 'input()'. */
|
||||
@JvmStatic
|
||||
public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||
return service<CommandLineHelper>().inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||
public fun inputString(editor: Editor, prompt: String, finishOn: Char?): String {
|
||||
return service<CommandLineHelper>().inputString(editor.vim, prompt, finishOn) ?: ""
|
||||
}
|
||||
|
||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||
@@ -216,65 +207,4 @@ 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.Util.enabledExtensions.add(name)
|
||||
PluginState.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(editor.getMode())) {
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
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.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
@@ -49,19 +49,17 @@ 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 {
|
||||
|
||||
object Util {
|
||||
companion object {
|
||||
fun doCommentary(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
selectionType: SelectionType,
|
||||
resetCaret: Boolean = true,
|
||||
resetCaret: Boolean,
|
||||
): Boolean {
|
||||
val mode = editor.vimStateMachine.mode
|
||||
if (mode !is Mode.VISUAL) {
|
||||
@@ -69,7 +67,8 @@ internal class CommentaryExtension : VimExtension {
|
||||
}
|
||||
|
||||
return runWriteAction {
|
||||
// Treat block- and character-wise selections as block comments. Fall back if the first action isn't available
|
||||
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
|
||||
// isn't available
|
||||
val actions = if (selectionType === SelectionType.LINE_WISE) {
|
||||
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
|
||||
} else {
|
||||
@@ -114,17 +113,12 @@ 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() {
|
||||
@@ -151,16 +145,6 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,13 +153,19 @@ 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 : ExtensionHandler {
|
||||
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
setOperatorFunction(this)
|
||||
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 {
|
||||
@@ -249,7 +239,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
*/
|
||||
private class CommentaryCommandAliasHandler : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
Util.doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||
doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,22 +19,24 @@ 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
|
||||
@@ -43,8 +45,6 @@ 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,13 +72,30 @@ 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())
|
||||
}
|
||||
|
||||
object Util {
|
||||
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)"
|
||||
|
||||
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)
|
||||
@@ -87,25 +104,18 @@ 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) {
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
setOperatorFunction(Operator(false))
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangeClearHandler : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
Util.clearExchange(editor.ij)
|
||||
clearExchange(editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +125,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 ?: SelectionType.CHARACTER_WISE)
|
||||
Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Operator(private val isVisual: Boolean = false) : OperatorFunction {
|
||||
private class Operator(private val isVisual: Boolean) : OperatorFunction {
|
||||
fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
|
||||
fun SelectionType.getString() = when (this) {
|
||||
SelectionType.CHARACTER_WISE -> "v"
|
||||
@@ -138,7 +148,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),
|
||||
@@ -148,12 +158,12 @@ internal class VimExchangeExtension : VimExtension {
|
||||
)
|
||||
}
|
||||
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(Util.EXCHANGE_KEY)
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
|
||||
if (exchange1 == null) {
|
||||
val highlighter = highlightExchange(currentExchange)
|
||||
currentExchange.setHighlighter(highlighter)
|
||||
ijEditor.putUserData(Util.EXCHANGE_KEY, currentExchange)
|
||||
ijEditor.putUserData(EXCHANGE_KEY, currentExchange)
|
||||
return true
|
||||
} else {
|
||||
val cmp = compareExchanges(exchange1, currentExchange)
|
||||
@@ -179,7 +189,7 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
exchange(ijEditor, ex1, ex2, reverse, expand)
|
||||
Util.clearExchange(ijEditor)
|
||||
clearExchange(ijEditor)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -344,14 +354,4 @@ 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(editor.mode)
|
||||
val isInOpPending = commandState.isOperatorPending
|
||||
|
||||
if (isInOpPending) {
|
||||
val matchitAction = MatchitAction()
|
||||
|
@@ -130,15 +130,15 @@ internal class NerdTree : VimExtension {
|
||||
addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
|
||||
addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
|
||||
|
||||
synchronized(Util.monitor) {
|
||||
Util.commandsRegistered = true
|
||||
synchronized(monitor) {
|
||||
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) {
|
||||
Util.callAction(editor, actionId, context)
|
||||
callAction(editor, actionId, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ internal class NerdTree : VimExtension {
|
||||
if (toolWindow.isVisible) {
|
||||
toolWindow.hide()
|
||||
} else {
|
||||
Util.callAction(editor, "ActivateProjectToolWindow", context)
|
||||
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(Util.monitor) {
|
||||
if (!Util.commandsRegistered) return
|
||||
synchronized(monitor) {
|
||||
if (!commandsRegistered) return
|
||||
installDispatcher(project)
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
val action = nextNode.actionHolder
|
||||
when (action) {
|
||||
is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
|
||||
is NerdAction.ToIj -> 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
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
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)
|
||||
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
},
|
||||
)
|
||||
registerCommand(
|
||||
@@ -502,11 +502,23 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
for (c in ('a'..'z') + ('A'..'Z')) {
|
||||
val ks = KeyStroke.getKeyStroke(c)
|
||||
if (ks !in actionsRoot) {
|
||||
registerCommand(c.toString(), NerdAction.Code { _, _, _ -> })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Util {
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
|
||||
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))
|
||||
@@ -521,51 +533,45 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +9,10 @@
|
||||
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
|
||||
@@ -21,10 +20,8 @@ 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"
|
||||
@@ -33,8 +30,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)
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler {
|
||||
@@ -48,21 +45,7 @@ internal class ParagraphMotion : VimExtension {
|
||||
}
|
||||
|
||||
fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? {
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)
|
||||
?.let { editor.normalizeOffset(it, true) }
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)?.let(editor::getLineEndForOffset)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@@ -8,26 +8,30 @@
|
||||
|
||||
package com.maddyhome.idea.vim.extension.replacewithregister
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
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.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
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
|
||||
@@ -35,10 +39,6 @@ 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,19 +53,17 @@ 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 ?: SelectionType.CHARACTER_WISE
|
||||
val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE
|
||||
editor.sortedCarets().forEach { caret ->
|
||||
val selectionStart = caret.selectionStart
|
||||
val selectionEnd = caret.selectionEnd
|
||||
|
||||
val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
||||
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||
}
|
||||
editor.exitVisualMode()
|
||||
}
|
||||
@@ -75,7 +73,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
override val isRepeatable: Boolean = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
setOperatorFunction(Operator())
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@@ -93,7 +91,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
||||
caretsAndSelections += visualSelection
|
||||
|
||||
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||
}
|
||||
|
||||
editor.sortedCarets().forEach { caret ->
|
||||
@@ -114,14 +112,14 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
editor.primaryCaret() to VimSelection.create(
|
||||
range.startOffset,
|
||||
range.endOffset - 1,
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
editor,
|
||||
),
|
||||
),
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
)
|
||||
// todo multicaret
|
||||
doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
|
||||
doReplace(ijEditor, editor.primaryCaret(), visualSelection)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -134,49 +132,52 @@ 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"
|
||||
@NonNls private const val OPERATOR_FUNC = "ReplaceWithRegisterOperatorFunc"
|
||||
@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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doReplace(editor: Editor, context: DataContext, 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,
|
||||
context.vim,
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
@@ -36,7 +36,6 @@ 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
|
||||
|
||||
|
||||
@@ -49,20 +48,17 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
|
||||
override fun init() {
|
||||
val highlightHandler = HighlightHandler()
|
||||
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)
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
|
||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
|
||||
|
||||
// workaround to support ; and , commands
|
||||
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("f", SneakMemoryHandler("f"))
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
|
||||
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
|
||||
}
|
||||
|
||||
private class SneakHandler(
|
||||
@@ -118,7 +114,7 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
var lastSymbols: String = ""
|
||||
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
||||
val caret = editor.primaryCaret()
|
||||
val position = caret.offset
|
||||
val position = caret.offset.point
|
||||
val chars = editor.text()
|
||||
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
||||
if (foundPosition != null) {
|
||||
@@ -277,18 +273,16 @@ 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, mappingModes: EnumSet<MappingMode>
|
||||
) {
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
mappingModes,
|
||||
MappingMode.NXO,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
mappingModes,
|
||||
MappingMode.NXO,
|
||||
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
|
||||
owner,
|
||||
handler,
|
||||
@@ -300,17 +294,17 @@ private fun VimExtension.mapToFunctionAndProvideKeys(
|
||||
// - 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 = mappingModes.filterNotTo(HashSet()) {
|
||||
val filteredModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
|
||||
}
|
||||
val filteredModes2 = mappingModes.filterNotTo(HashSet()) {
|
||||
val filteredModes2 = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
||||
}
|
||||
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
||||
val filteredFromModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
injector.keyGroup.hasmapfrom(it, fromKeys)
|
||||
}
|
||||
|
||||
val doubleFiltered = mappingModes
|
||||
val doubleFiltered = MappingMode.NXO
|
||||
.filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
|
||||
.toSet()
|
||||
putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.extension.surround
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
@@ -18,7 +17,6 @@ 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
|
||||
@@ -26,16 +24,14 @@ 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.group.findBlockRange
|
||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
@@ -46,6 +42,7 @@ 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.mode
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.awt.event.KeyEvent
|
||||
@@ -81,15 +78,13 @@ 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) {
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +96,7 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val ijEditor = editor.ij
|
||||
val c = getChar(ijEditor)
|
||||
if (c.code == 0) return
|
||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return
|
||||
val pair = getOrInputPair(c, ijEditor) ?: return
|
||||
|
||||
editor.forEachCaret {
|
||||
val line = it.getBufferPosition().line
|
||||
@@ -151,7 +146,7 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val charTo = getChar(editor.ij)
|
||||
if (charTo.code == 0) return
|
||||
|
||||
val newSurround = getOrInputPair(charTo, editor.ij, context.ij) ?: return
|
||||
val newSurround = getOrInputPair(charTo, editor.ij) ?: return
|
||||
runWriteAction { change(editor, context, charFrom, newSurround) }
|
||||
}
|
||||
|
||||
@@ -228,12 +223,12 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val searchHelper = injector.searchHelper
|
||||
return when (char) {
|
||||
't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
|
||||
'(', ')', 'b' -> findBlockRange(editor, caret, '(', 1, true)
|
||||
'[', ']' -> findBlockRange(editor, caret, '[', 1, true)
|
||||
'{', '}', 'B' -> findBlockRange(editor, caret, '{', 1, true)
|
||||
'<', '>' -> findBlockRange(editor, caret, '<', 1, true)
|
||||
'(', ')', 'b' -> searchHelper.findBlockRange(editor, caret, '(', 1, true)
|
||||
'[', ']' -> searchHelper.findBlockRange(editor, caret, '[', 1, true)
|
||||
'{', '}', 'B' -> searchHelper.findBlockRange(editor, caret, '{', 1, true)
|
||||
'<', '>' -> searchHelper.findBlockRange(editor, caret, '<', 1, true)
|
||||
'`', '\'', '"' -> {
|
||||
val caretOffset = caret.offset
|
||||
val caretOffset = caret.offset.point
|
||||
val text = editor.text()
|
||||
if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
|
||||
TextRange(caretOffset - 1, caretOffset + 1)
|
||||
@@ -270,23 +265,23 @@ internal class VimSurroundExtension : VimExtension {
|
||||
|
||||
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
||||
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val ijEditor = vimEditor.ij
|
||||
val c = getChar(ijEditor)
|
||||
val editor = vimEditor.ij
|
||||
val c = getChar(editor)
|
||||
if (c.code == 0) return true
|
||||
|
||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
||||
val pair = getOrInputPair(c, editor) ?: return false
|
||||
|
||||
runWriteAction {
|
||||
val change = VimPlugin.getChange()
|
||||
if (supportsMultipleCursors) {
|
||||
ijEditor.runWithEveryCaretAndRestore {
|
||||
applyOnce(ijEditor, change, pair, count)
|
||||
editor.runWithEveryCaretAndRestore {
|
||||
applyOnce(editor, change, pair, count)
|
||||
}
|
||||
}
|
||||
else {
|
||||
applyOnce(ijEditor, change, pair, count)
|
||||
applyOnce(editor, change, pair, count)
|
||||
// Jump back to start
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -320,9 +315,7 @@ private val LOG = logger<VimSurroundExtension>()
|
||||
|
||||
private const val REGISTER = '"'
|
||||
|
||||
private const val OPERATOR_FUNC = "SurroundOperatorFunc"
|
||||
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
|
||||
private val SURROUND_PAIRS = mapOf(
|
||||
'b' to ("(" to ")"),
|
||||
@@ -348,8 +341,8 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
|
||||
null
|
||||
}
|
||||
|
||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
||||
val tagInput = inputString(editor, context, "<", '>')
|
||||
private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
||||
val tagInput = inputString(editor, "<", '>')
|
||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||
return if (matcher.find()) {
|
||||
val tagName = matcher.group(1)
|
||||
@@ -362,18 +355,17 @@ private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, Str
|
||||
|
||||
private fun inputFunctionName(
|
||||
editor: Editor,
|
||||
context: DataContext,
|
||||
withInternalSpaces: Boolean,
|
||||
): Pair<String, String>? {
|
||||
val functionNameInput = inputString(editor, context, "function: ", null)
|
||||
val functionNameInput = inputString(editor, "function: ", null)
|
||||
if (functionNameInput.isEmpty()) return null
|
||||
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
||||
}
|
||||
|
||||
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
|
||||
'<', 't' -> inputTagPair(editor, context)
|
||||
'f' -> inputFunctionName(editor, context, false)
|
||||
'F' -> inputFunctionName(editor, context, true)
|
||||
private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = when (c) {
|
||||
'<', 't' -> inputTagPair(editor)
|
||||
'f' -> inputFunctionName(editor, false)
|
||||
'F' -> inputFunctionName(editor, true)
|
||||
else -> getSurroundPair(c)
|
||||
}
|
||||
|
||||
|
@@ -138,7 +138,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
((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(editor.getMode())) {
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
|
@@ -68,11 +68,10 @@ 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
|
||||
@@ -197,7 +196,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val allowWrap = injector.options(editor).whichwrap.contains("~")
|
||||
var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
|
||||
if (motion is Motion.Error) return false
|
||||
changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||
changeCase(editor, caret, caret.offset.point, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||
motion = injector.motion.getHorizontalMotion(
|
||||
editor,
|
||||
caret,
|
||||
@@ -235,7 +234,8 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
val lineLength = editor.lineLength(line)
|
||||
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
|
||||
val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
|
||||
val pad =
|
||||
EditorHelper.pad((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context, line, column)
|
||||
val offset = editor.getLineEndOffset(line)
|
||||
insertText(editor, caret, offset, pad)
|
||||
}
|
||||
@@ -394,7 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
) {
|
||||
val startPos = editor.offsetToBufferPosition(caret.offset)
|
||||
val startPos = editor.offsetToBufferPosition(caret.offset.point)
|
||||
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
||||
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
@@ -450,7 +450,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
dir: Int,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
val start = caret.offset
|
||||
val start = caret.offset.point
|
||||
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
|
||||
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
|
||||
}
|
||||
@@ -484,7 +484,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
|
||||
// Remember the current caret column
|
||||
val intendedColumn = caret.vimLastColumn
|
||||
val indentConfig = create((editor as IjVimEditor).editor)
|
||||
val indentConfig = create((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context)
|
||||
val sline = editor.offsetToBufferPosition(range.startOffset).line
|
||||
val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
|
||||
val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
|
||||
@@ -573,62 +573,48 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
val startOffset = editor.getLineStartOffset(startLine)
|
||||
val endOffset = editor.getLineEndOffset(endLine)
|
||||
return sortTextRange(editor, caret, startOffset, endOffset, lineComparator, sortOptions)
|
||||
}
|
||||
|
||||
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()
|
||||
/**
|
||||
* 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()
|
||||
var previous: String? = null
|
||||
while (iterator.hasNext()) {
|
||||
val current = iterator.next()
|
||||
if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
if (current == previous || sortOption.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
previous = current
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sortedLines.isEmpty()) {
|
||||
if (lines.size < 1) {
|
||||
return false
|
||||
}
|
||||
replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
|
||||
replaceText(editor, caret, start, end, StringUtil.join(lines, "\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,11 +8,9 @@
|
||||
|
||||
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,9 +11,6 @@ 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;
|
||||
@@ -25,10 +22,7 @@ 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.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.helper.*;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener;
|
||||
@@ -40,10 +34,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;
|
||||
|
||||
/**
|
||||
@@ -210,8 +204,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
|
||||
public void editorCreated(@NotNull Editor editor) {
|
||||
UserDataManager.setVimInitialised(editor, true);
|
||||
|
||||
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
||||
VimPlugin.getKey().registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
|
||||
initLineNumbers(editor);
|
||||
@@ -235,7 +228,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
|
||||
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
|
||||
Runnable switchToInsertMode = () -> {
|
||||
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
|
||||
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
||||
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
|
||||
KeyHandler.getInstance().reset(new IjVimEditor(editor));
|
||||
};
|
||||
@@ -253,13 +246,14 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
switchToInsertMode.run();
|
||||
}
|
||||
});
|
||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||
updateCaretsVisualAttributes(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);
|
||||
}
|
||||
|
||||
@@ -290,18 +284,6 @@ 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();
|
||||
|
||||
@@ -342,45 +324,20 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public @NotNull Collection<VimEditor> getEditorsRaw() {
|
||||
return getLocalEditors()
|
||||
public Collection<VimEditor> localEditors() {
|
||||
return HelperKt.localEditors().stream()
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors() {
|
||||
return getLocalEditors()
|
||||
.filter(UserDataManager::getVimInitialised)
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||
public Collection<VimEditor> localEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return getLocalEditors()
|
||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||
return HelperKt.localEditors(document).stream()
|
||||
.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();
|
||||
}
|
||||
}
|
||||
|
@@ -22,17 +22,16 @@ import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
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;
|
||||
@@ -41,13 +40,10 @@ 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
@@ -442,35 +438,14 @@ public class FileGroup extends VimFileBase {
|
||||
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||
|
||||
/**
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VimEditor selectEditor(@NotNull String projectId, @NotNull String documentPath, @Nullable String protocol) {
|
||||
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
|
||||
if (fileSystem == null) return null;
|
||||
VirtualFile virtualFile = fileSystem.findFileByPath(documentPath);
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
Project project = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
|
||||
.filter(p -> injector.getFile().getProjectId(p).equals(projectId))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
Editor editor = selectEditor(project, virtualFile);
|
||||
if (editor == null) return null;
|
||||
return new IjVimEditor(editor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getProjectId(@NotNull Object project) {
|
||||
if (!(project instanceof Project)) throw new IllegalArgumentException();
|
||||
return ((Project) project).getName();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@ 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)
|
||||
@@ -28,15 +29,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 useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,6 +33,8 @@ 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)
|
||||
)
|
||||
@@ -79,16 +81,13 @@ 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))
|
||||
|
||||
// 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))
|
||||
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))
|
||||
|
||||
// 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.Util.logTrackedAction(actionId)
|
||||
ActionTracker.logTrackedAction(actionId)
|
||||
}
|
||||
|
||||
override fun logCopiedAction(actionId: String) {
|
||||
ActionTracker.Util.logCopiedAction(actionId)
|
||||
ActionTracker.logCopiedAction(actionId)
|
||||
}
|
||||
|
||||
override fun setIfLoopUsed(value: Boolean) {
|
||||
VimscriptState.Util.isLoopUsed = value
|
||||
VimscriptState.isLoopUsed = value
|
||||
}
|
||||
|
||||
override fun setIfMapExprUsed(value: Boolean) {
|
||||
VimscriptState.Util.isMapExprUsed = value
|
||||
VimscriptState.isMapExprUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionCallUsed(value: Boolean) {
|
||||
VimscriptState.Util.isFunctionCallUsed = value
|
||||
VimscriptState.isFunctionCallUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionDeclarationUsed(value: Boolean) {
|
||||
VimscriptState.Util.isFunctionDeclarationUsed = value
|
||||
VimscriptState.isFunctionDeclarationUsed = value
|
||||
}
|
||||
|
||||
override fun setIfIfUsed(value: Boolean) {
|
||||
VimscriptState.Util.isIfUsed = value
|
||||
VimscriptState.isIfUsed = value
|
||||
}
|
||||
|
||||
override fun addExtensionEnabledWithPlug(extension: String) {
|
||||
VimscriptState.Util.extensionsEnabledWithPlug.add(extension)
|
||||
VimscriptState.extensionsEnabledWithPlug.add(extension)
|
||||
}
|
||||
|
||||
override fun addSourcedFile(path: String) {
|
||||
VimscriptState.Util.sourcedFiles.add(path)
|
||||
VimscriptState.sourcedFiles.add(path)
|
||||
}
|
||||
}
|
||||
|
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.lang.CodeDocumentationAwareCommenter
|
||||
import com.intellij.lang.LanguageCommenters
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimPsiService
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
|
||||
@Service
|
||||
public class IjVimPsiService: VimPsiService {
|
||||
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
|
||||
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
||||
val psiElement = psiFile.findElementAt(pos) ?: return null
|
||||
val language = psiElement.language
|
||||
val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
|
||||
val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
|
||||
val commentText = psiComment.text
|
||||
|
||||
val blockCommentPrefix = commenter.blockCommentPrefix
|
||||
val blockCommentSuffix = commenter.blockCommentSuffix
|
||||
|
||||
val docCommentPrefix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentPrefix
|
||||
val docCommentSuffix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentSuffix
|
||||
|
||||
val prefixToSuffix: Pair<String, String>? =
|
||||
if (docCommentPrefix != null && docCommentSuffix != null && commentText.startsWith(docCommentPrefix) && commentText.endsWith(docCommentSuffix)) {
|
||||
docCommentPrefix to docCommentSuffix
|
||||
}
|
||||
else if (blockCommentPrefix != null && blockCommentSuffix != null && commentText.startsWith(blockCommentPrefix) && commentText.endsWith(blockCommentSuffix)) {
|
||||
blockCommentPrefix to blockCommentSuffix
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
return Pair(psiComment.textRange.vim, prefixToSuffix)
|
||||
}
|
||||
|
||||
override fun getDoubleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
|
||||
// TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
|
||||
return getDoubleQuotesRangeNoPSI(editor.text(), pos, isInner)
|
||||
}
|
||||
|
||||
override fun getSingleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
|
||||
// TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
|
||||
return getSingleQuotesRangeNoPSI(editor.text(), pos, isInner)
|
||||
}
|
||||
}
|
@@ -26,12 +26,10 @@ 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.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.api.*;
|
||||
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;
|
||||
@@ -101,9 +99,9 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
|
||||
@Override
|
||||
public void updateShortcutKeysRegistration() {
|
||||
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
||||
unregisterShortcutKeys(editor);
|
||||
registerRequiredShortcutKeys(editor);
|
||||
for (Editor editor : HelperKt.localEditors()) {
|
||||
unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +228,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.getApplication().isOctopusEnabled() ||
|
||||
if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
|
||||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
|
||||
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
|
||||
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
|
||||
|
@@ -10,7 +10,6 @@ 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
|
||||
@@ -27,7 +26,6 @@ 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
|
||||
@@ -78,12 +76,11 @@ 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)
|
||||
keyHandler.handleKey(editor, key, context, keyHandler.keyHandlerState)
|
||||
getInstance().handleKey(editor, key, context)
|
||||
}
|
||||
if (injector.messages.isError()) return@runnable
|
||||
}
|
||||
|
@@ -8,27 +8,43 @@
|
||||
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
|
||||
import com.intellij.openapi.editor.VisualPosition
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import com.intellij.util.MathUtil.clamp
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.BufferPosition
|
||||
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.VimChangeGroupBase
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||
import com.maddyhome.idea.vim.api.addJump
|
||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
||||
import com.maddyhome.idea.vim.api.getJump
|
||||
import com.maddyhome.idea.vim.api.getJumpSpot
|
||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||
import com.maddyhome.idea.vim.api.getVisualLineCount
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.lineLength
|
||||
import com.maddyhome.idea.vim.api.normalizeColumn
|
||||
import com.maddyhome.idea.vim.api.normalizeLine
|
||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||
import com.maddyhome.idea.vim.api.normalizeVisualColumn
|
||||
import com.maddyhome.idea.vim.api.normalizeVisualLine
|
||||
import com.maddyhome.idea.vim.api.options
|
||||
import com.maddyhome.idea.vim.api.visualLineToBufferLine
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.MotionType
|
||||
@@ -37,9 +53,12 @@ import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.handler.Motion
|
||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||
import com.maddyhome.idea.vim.handler.Motion.AdjustedOffset
|
||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.handler.toMotionOrError
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||
@@ -47,25 +66,46 @@ import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
||||
import com.maddyhome.idea.vim.mark.Mark
|
||||
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.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import org.jetbrains.annotations.Range
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
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)
|
||||
}
|
||||
|
||||
private fun selectEditor(project: Project, mark: Mark): Editor? {
|
||||
val virtualFile = markToVirtualFile(mark) ?: return null
|
||||
return selectEditor(project, virtualFile)
|
||||
}
|
||||
|
||||
private fun markToVirtualFile(mark: Mark): VirtualFile? {
|
||||
val protocol = mark.protocol
|
||||
val fileSystem: VirtualFileSystem? = VirtualFileManager.getInstance().getFileSystem(protocol)
|
||||
return fileSystem?.findFileByPath(mark.filepath)
|
||||
}
|
||||
|
||||
private fun selectEditor(project: Project?, file: VirtualFile) =
|
||||
VimPlugin.getFile().selectEditor(project, file)
|
||||
|
||||
override fun moveCaretToMatchingPair(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
return SearchHelper.findMatchingPairOnCurrentLine(editor.ij, caret.ij).toMotionOrError()
|
||||
}
|
||||
|
||||
override fun moveCaretToFirstDisplayLine(
|
||||
editor: VimEditor,
|
||||
caret: ImmutableVimCaret,
|
||||
@@ -88,12 +128,85 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
return moveCaretToScreenLocation(editor.ij, caret.ij, ScreenLocation.MIDDLE, 0, false)
|
||||
}
|
||||
|
||||
override fun moveCaretToMark(caret: ImmutableVimCaret, ch: Char, toLineStart: Boolean): Motion {
|
||||
val markService = injector.markService
|
||||
val mark = markService.getMark(caret, ch) ?: return Motion.Error
|
||||
|
||||
val caretEditor = caret.editor
|
||||
val caretVirtualFile = EditorHelper.getVirtualFile((caretEditor as IjVimEditor).editor)
|
||||
|
||||
val line = mark.line
|
||||
|
||||
if (caretVirtualFile!!.path == mark.filepath) {
|
||||
val offset = if (toLineStart) {
|
||||
moveCaretToLineStartSkipLeading(caretEditor, line)
|
||||
} else {
|
||||
caretEditor.bufferPositionToOffset(BufferPosition(line, mark.col, false))
|
||||
}
|
||||
return offset.toMotionOrError()
|
||||
}
|
||||
|
||||
val project = caretEditor.editor.project
|
||||
val markEditor = selectEditor(project!!, mark)
|
||||
if (markEditor != null) {
|
||||
// todo should we move all the carets or only one?
|
||||
for (carett in markEditor.caretModel.allCarets) {
|
||||
val offset = if (toLineStart) {
|
||||
moveCaretToLineStartSkipLeading(IjVimEditor(markEditor), line)
|
||||
} else {
|
||||
// todo should it be the same as getting offset above?
|
||||
markEditor.logicalPositionToOffset(LogicalPosition(line, mark.col))
|
||||
}
|
||||
IjVimCaret(carett!!).moveToOffset(offset)
|
||||
}
|
||||
}
|
||||
return Motion.Error
|
||||
}
|
||||
|
||||
override fun moveCaretToJump(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Motion {
|
||||
val jumpService = injector.jumpService
|
||||
val spot = jumpService.getJumpSpot(editor)
|
||||
val (line, col, fileName) = jumpService.getJump(editor, count) ?: return Motion.Error
|
||||
val vf = EditorHelper.getVirtualFile(editor.ij) ?: return Motion.Error
|
||||
val lp = BufferPosition(line, col, false)
|
||||
val lpNative = LogicalPosition(line, col, false)
|
||||
return if (vf.path != fileName) {
|
||||
val newFile = LocalFileSystem.getInstance().findFileByPath(fileName.replace(File.separatorChar, '/'))
|
||||
?: return Motion.Error
|
||||
selectEditor(editor.ij.project, newFile)?.let { newEditor ->
|
||||
if (spot == -1) {
|
||||
jumpService.addJump(editor, false)
|
||||
}
|
||||
newEditor.vim.let {
|
||||
it.currentCaret().moveToOffset(it.normalizeOffset(newEditor.logicalPositionToOffset(lpNative), false))
|
||||
}
|
||||
}
|
||||
Motion.Error
|
||||
} else {
|
||||
if (spot == -1) {
|
||||
jumpService.addJump(editor, false)
|
||||
}
|
||||
editor.bufferPositionToOffset(lp).toMotionOrError()
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveCaretToCurrentDisplayLineMiddle(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
|
||||
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
||||
return moveCaretToColumn(editor, caret, max(0, min(len - 1, width)), false)
|
||||
}
|
||||
|
||||
override fun moveCaretToColumn(editor: VimEditor, caret: ImmutableVimCaret, count: Int, allowEnd: Boolean): Motion {
|
||||
val line = caret.getLine().line
|
||||
val column = editor.normalizeColumn(line, count, allowEnd)
|
||||
val offset = editor.bufferPositionToOffset(BufferPosition(line, column, false))
|
||||
return if (column != count) {
|
||||
AdjustedOffset(offset, count)
|
||||
} else {
|
||||
AbsoluteOffset(offset)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||
return moveCaretToColumn(editor, caret, col, false)
|
||||
@@ -104,7 +217,7 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||
val bufferLine = caret.getLine()
|
||||
val bufferLine = caret.getLine().line
|
||||
return editor.getLeadingCharacterOffset(bufferLine, col)
|
||||
}
|
||||
|
||||
@@ -117,6 +230,36 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
return moveCaretToColumn(editor, caret, col, allowEnd)
|
||||
}
|
||||
|
||||
override fun moveCaretToLineWithSameColumn(
|
||||
editor: VimEditor,
|
||||
line: Int,
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
var c = caret.vimLastColumn
|
||||
var l = line
|
||||
if (l < 0) {
|
||||
l = 0
|
||||
c = 0
|
||||
} else if (l >= editor.lineCount()) {
|
||||
l = editor.normalizeLine(editor.lineCount() - 1)
|
||||
c = editor.lineLength(l)
|
||||
}
|
||||
val newPos = BufferPosition(l, editor.normalizeColumn(l, c, false))
|
||||
return editor.bufferPositionToOffset(newPos)
|
||||
}
|
||||
|
||||
override fun moveCaretToLineWithStartOfLineOption(
|
||||
editor: VimEditor,
|
||||
line: Int,
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
return if (injector.options(editor).startofline) {
|
||||
moveCaretToLineStartSkipLeading(editor, line)
|
||||
} else {
|
||||
moveCaretToLineWithSameColumn(editor, line, caret)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
|
||||
*/
|
||||
@@ -134,18 +277,30 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
}
|
||||
|
||||
override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||
switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
|
||||
return editor.currentCaret().offset
|
||||
return editor.currentCaret().offset.point
|
||||
}
|
||||
|
||||
override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||
val absolute = rawCount >= 1
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||
switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
|
||||
return editor.currentCaret().offset
|
||||
return editor.currentCaret().offset.point
|
||||
}
|
||||
|
||||
override fun moveCaretToLinePercent(
|
||||
editor: VimEditor,
|
||||
caret: ImmutableVimCaret,
|
||||
count: Int,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
return moveCaretToLineWithStartOfLineOption(
|
||||
editor,
|
||||
editor.normalizeLine((editor.lineCount() * clamp(count, 0, 100) + 99) / 100 - 1),
|
||||
caret,
|
||||
)
|
||||
}
|
||||
|
||||
private enum class ScreenLocation {
|
||||
|
@@ -21,7 +21,6 @@ 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
|
||||
@@ -56,7 +55,6 @@ 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")
|
||||
@@ -278,7 +276,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
ActionTracker.Util.logTrackedAction(id)
|
||||
ActionTracker.logTrackedAction(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +284,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
|
||||
if (id != null) {
|
||||
ActionTracker.Util.logCopiedAction(id)
|
||||
ActionTracker.logCopiedAction(id)
|
||||
}
|
||||
notification?.expire()
|
||||
|
||||
|
@@ -20,7 +20,6 @@ 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
|
||||
@@ -38,6 +37,7 @@ 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,27 +85,24 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
modeBeforeCommandProcessing = currentMode
|
||||
val initText = getRange(editor, cmd)
|
||||
injector.markService.setVisualSelectionMarks(editor)
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.vimStateMachine.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, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke): 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) {
|
||||
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
}
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
|
||||
return true
|
||||
} else {
|
||||
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||
lambdaEditor.mode = NORMAL()
|
||||
getInstance().reset(lambdaEditor)
|
||||
}
|
||||
getInstance(editor).mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -115,7 +112,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
panel.deactivate(true)
|
||||
var res = true
|
||||
try {
|
||||
editor.mode = NORMAL()
|
||||
getInstance(editor).mode = NORMAL()
|
||||
|
||||
logger.debug("processing command")
|
||||
|
||||
@@ -155,7 +152,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
}
|
||||
|
||||
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
|
||||
editor.mode = NORMAL()
|
||||
editor.vimStateMachine.mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.deactivate(true, resetCaret)
|
||||
@@ -165,7 +162,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.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.vimStateMachine.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,10 +37,6 @@ 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,6 +21,8 @@ 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.*;
|
||||
@@ -31,11 +33,12 @@ 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.*;
|
||||
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.options.GlobalOptionChangeListener;
|
||||
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.regexp.*;
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext;
|
||||
@@ -56,6 +59,7 @@ 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;
|
||||
@@ -538,24 +542,20 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
*
|
||||
* @param editor The editor to search in
|
||||
* @param caret The caret to use for initial search offset, and to move for interactive substitution
|
||||
* @param context
|
||||
* @param range Only search and substitute within the given line range. Must be valid
|
||||
* @param excmd The command part of the ex command line, e.g. `s` or `substitute`, or `~`
|
||||
* @param exarg The argument to the substitute command, such as `/{pattern}/{string}/[flags]`
|
||||
* @return True if the substitution succeeds, false on error. Will succeed even if nothing is modified
|
||||
* @return True if the substitution succeeds, false on error. Will succeed even if nothing is modified
|
||||
*/
|
||||
@Override
|
||||
@RWLockLabel.SelfSynchronized
|
||||
public boolean processSubstituteCommand(@NotNull VimEditor editor,
|
||||
@NotNull VimCaret caret,
|
||||
@NotNull ExecutionContext context,
|
||||
@NotNull LineRange range,
|
||||
@NotNull @NonNls String excmd,
|
||||
@NotNull @NonNls String exarg,
|
||||
@NotNull VimLContext parent) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
return super.processSubstituteCommand(editor, caret, context, range, excmd, exarg, parent);
|
||||
}
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.processSubstituteCommand(editor, caret, range, excmd, exarg, parent);
|
||||
|
||||
// Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
|
||||
List<ExException> exceptions = new ArrayList<>();
|
||||
@@ -812,7 +812,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
RangeHighlighter hl =
|
||||
SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
|
||||
endoff);
|
||||
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), context, match, ((IjVimCaret)caret).getCaret(), startoff);
|
||||
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), match, ((IjVimCaret)caret).getCaret(), startoff);
|
||||
((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
|
||||
switch (choice) {
|
||||
case SUBSTITUTE_THIS:
|
||||
@@ -841,7 +841,8 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
caret.moveToOffset(startoff);
|
||||
if (expression != null) {
|
||||
try {
|
||||
match = expression.evaluate(editor, context, parent).toInsertableString();
|
||||
match =
|
||||
expression.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent).toInsertableString();
|
||||
}
|
||||
catch (Exception e) {
|
||||
exceptions.add((ExException)e);
|
||||
@@ -992,9 +993,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
|
||||
}
|
||||
|
||||
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor,
|
||||
@NotNull ExecutionContext context,
|
||||
@NotNull String match, @NotNull Caret caret, int startoff) {
|
||||
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
|
||||
final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
|
||||
final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
|
||||
final ReplaceConfirmationChoice choice;
|
||||
@@ -1028,6 +1027,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
else {
|
||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
||||
final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
|
||||
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
||||
exEntryPanel.activate(editor, ((IjEditorExecutionContext)context).getContext(), MessageHelper.message("replace.with.0", match), "", 1);
|
||||
new IjVimCaret(caret).moveToOffset(startoff);
|
||||
ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
|
||||
@@ -1085,9 +1085,9 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
||||
private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
|
||||
if (forwards) {
|
||||
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
|
||||
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset(), count, searchOptions);
|
||||
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset().getPoint(), count, searchOptions);
|
||||
} else {
|
||||
return searchBackward(editor, editor.primaryCaret().getOffset(), count);
|
||||
return searchBackward(editor, editor.primaryCaret().getOffset().getPoint(), count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1200,50 +1200,47 @@ 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) {
|
||||
// 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 (Project project : ProjectManager.getInstance().getOpenProjects()) {
|
||||
final Document document = event.getDocument();
|
||||
|
||||
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);
|
||||
for (Editor editor : localEditors(document, project)) {
|
||||
Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
if (hls == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("hls=" + hls);
|
||||
logger.debug("event=" + event);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
|
||||
logger.debug("hls=" + hls);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -111,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
}
|
||||
|
||||
private fun buildJump(place: PlaceInfo): Jump? {
|
||||
val editor = injector.editorGroup.getEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val editor = injector.editorGroup.localEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val offset = place.caretPosition?.startOffset ?: return null
|
||||
|
||||
val bufferPosition = editor.offsetToBufferPosition(offset)
|
||||
|
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
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
|
||||
@@ -19,7 +18,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.EditorFactory
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
@@ -29,11 +28,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
|
||||
@@ -194,10 +193,6 @@ 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) {
|
||||
@@ -205,18 +200,15 @@ 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 = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromDelete(anEditor, event.offset, event.oldLength)
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromDelete(IjVimEditor(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) {
|
||||
@@ -224,19 +216,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 = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromInsert(anEditor, event.offset, event.newLength)
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromInsert(IjVimEditor(anEditor), event.offset, event.newLength)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) }
|
||||
private fun getAnEditor(doc: Document): Editor? {
|
||||
val editors = localEditors(doc)
|
||||
return if (editors.size > 0) {
|
||||
editors[0]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
|
||||
|
@@ -8,11 +8,9 @@
|
||||
|
||||
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,7 +14,6 @@ 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
|
||||
@@ -52,7 +51,6 @@ 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,8 +20,9 @@ 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.vimDisabled
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.OptionConstants
|
||||
@@ -29,6 +30,7 @@ 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
|
||||
@@ -53,7 +55,9 @@ internal object IdeaSelectionControl {
|
||||
selectionSource: VimListenerManager.SelectionSource = VimListenerManager.SelectionSource.OTHER,
|
||||
) {
|
||||
VimVisualTimer.singleTask(editor.vim.mode) { initialMode ->
|
||||
if (vimDisabled(editor)) return@singleTask
|
||||
|
||||
if (VimPlugin.isNotEnabled()) return@singleTask
|
||||
if (editor.isIdeaVimDisabledHere) return@singleTask
|
||||
|
||||
logger.debug("Adjust non-vim selection. Source: $selectionSource, initialMode: $initialMode")
|
||||
|
||||
@@ -75,7 +79,7 @@ internal object IdeaSelectionControl {
|
||||
|
||||
logger.debug("Some carets have selection. State before adjustment: ${editor.vim.mode}")
|
||||
|
||||
editor.vim.mode = Mode.NORMAL()
|
||||
editor.vim.vimStateMachine.mode = Mode.NORMAL()
|
||||
|
||||
activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
|
||||
} else {
|
||||
@@ -117,7 +121,7 @@ internal object IdeaSelectionControl {
|
||||
is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
|
||||
is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
|
||||
is Mode.INSERT -> VimPlugin.getChange()
|
||||
.insertBeforeCursor(editor.vim, injector.executionContextManager.getEditorExecutionContext(editor.vim))
|
||||
.insertBeforeCursor(editor.vim, injector.executionContextManager.onEditor(editor.vim))
|
||||
|
||||
is Mode.NORMAL -> Unit
|
||||
else -> error("Unexpected mode: $mode")
|
||||
|
@@ -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,6 +27,7 @@ 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
|
||||
@@ -38,6 +39,7 @@ 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
|
||||
|
||||
@@ -337,9 +339,8 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
|
||||
|
||||
override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
||||
val enterKey = key(key)
|
||||
val context = dataContext?.vim ?: injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
|
||||
val context = injector.executionContextManager.onEditor(editor.vim, dataContext?.vim)
|
||||
KeyHandler.getInstance().handleKey(editor.vim, enterKey, context)
|
||||
}
|
||||
|
||||
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
|
||||
@@ -361,4 +362,4 @@ internal fun isOctopusEnabled(s: KeyStroke, editor: Editor): Boolean {
|
||||
}
|
||||
|
||||
internal val enableOctopus: Boolean
|
||||
get() = injector.application.isOctopusEnabled()
|
||||
get() = injector.globalOptions().octopushandler
|
||||
|
@@ -8,7 +8,6 @@
|
||||
|
||||
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
|
||||
@@ -19,17 +18,14 @@ 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
|
||||
|
||||
@@ -89,12 +85,7 @@ 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
|
||||
// NOTE: At the moment, this causes project leak in tests
|
||||
// IJPL-928 - this will be fixed in 2024.2
|
||||
// [VERSION UPDATE] 2024.2 - remove if wrapping
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
(this as? EditorEx)?.setCaretVisible(true)
|
||||
}
|
||||
(this as? EditorEx)?.setCaretVisible(true)
|
||||
}
|
||||
|
||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
|
||||
@@ -143,31 +134,3 @@ 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()
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,8 +11,8 @@ package com.maddyhome.idea.vim.helper
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.maddyhome.idea.vim.action.change.Extension
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||
@@ -23,7 +23,7 @@ import javax.swing.KeyStroke
|
||||
@Service
|
||||
internal class CommandLineHelper : VimCommandLineHelper {
|
||||
|
||||
override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
|
||||
override fun inputString(vimEditor: VimEditor, prompt: String, finishOn: Char?): String? {
|
||||
val editor = vimEditor.ij
|
||||
if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
|
||||
val input = Extension.consumeString()
|
||||
@@ -53,7 +53,7 @@ internal class CommandLineHelper : VimCommandLineHelper {
|
||||
var text: String? = null
|
||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
|
||||
val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
|
||||
exEntryPanel.activate(editor, context.ij, prompt.ifEmpty { " " }, "", 1)
|
||||
exEntryPanel.activate(editor, injector.executionContextManager.onEditor(editor.vim).ij, prompt.ifEmpty { " " }, "", 1)
|
||||
ModalEntry.activate(editor.vim) { key: KeyStroke ->
|
||||
return@activate when {
|
||||
key.isCloseKeyStroke() -> {
|
||||
|
@@ -20,6 +20,7 @@ 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) {
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,7 +14,6 @@ import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
|
||||
@Deprecated("Do not use context wrappers, use existing provided contexts. If no context available, use `injector.getExecutionContextManager().getEditorExecutionContext(editor)`")
|
||||
internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
|
||||
private val editor: Editor,
|
||||
private val editorContext: DataContext,
|
||||
|
@@ -17,7 +17,6 @@ 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;
|
||||
@@ -30,7 +29,6 @@ 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;
|
||||
|
||||
@@ -199,7 +197,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 VimEditor getEditor(final @Nullable VirtualFile file) {
|
||||
public static @Nullable Editor getEditor(final @Nullable VirtualFile file) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -208,15 +206,23 @@ public class EditorHelper {
|
||||
if (doc == null) {
|
||||
return null;
|
||||
}
|
||||
return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
|
||||
final List<Editor> editors = HelperKt.localEditors(doc);
|
||||
if (editors.size() > 0) {
|
||||
return editors.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @NotNull String pad(final @NotNull Editor editor, int line, final int to) {
|
||||
public static @NotNull String pad(final @NotNull Editor editor,
|
||||
@NotNull DataContext context,
|
||||
int line,
|
||||
final int to) {
|
||||
final int len = EngineEditorHelperKt.lineLength(new IjVimEditor(editor), line);
|
||||
if (len >= to) return "";
|
||||
|
||||
final int limit = to - len;
|
||||
return IndentConfig.create(editor).createIndentBySize(limit);
|
||||
return IndentConfig.create(editor, context).createIndentBySize(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -17,7 +17,6 @@ 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
|
||||
@@ -38,27 +37,25 @@ public val Editor.fileSize: Int
|
||||
internal val Editor.isIdeaVimDisabledHere: Boolean
|
||||
get() {
|
||||
val ideaVimSupportValue = injector.globalIjOptions().ideavimsupport
|
||||
return (ideaVimDisabledInDialog(ideaVimSupportValue) && isInDialog()) ||
|
||||
!ClientId.isCurrentlyUnderLocalId || // CWM-927
|
||||
(ideaVimDisabledForSingleLine(ideaVimSupportValue) && isSingleLine())
|
||||
return disabledInDialog ||
|
||||
(!ClientId.isCurrentlyUnderLocalId) || // CWM-927
|
||||
(!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isDatabaseCell()) ||
|
||||
(!ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_singleline) && isOneLineMode)
|
||||
}
|
||||
|
||||
private fun ideaVimDisabledInDialog(ideaVimSupportValue: StringListOptionValue): Boolean {
|
||||
return !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||
&& !ideaVimSupportValue.contains(IjOptionConstants.ideavimsupport_dialoglegacy)
|
||||
private fun Editor.isDatabaseCell(): Boolean {
|
||||
return isTableCellEditor(this.component)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the editor is a primary editor in the main editing area.
|
||||
|
@@ -9,12 +9,19 @@
|
||||
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
|
||||
|
||||
@@ -29,6 +36,34 @@ 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
|
||||
|
@@ -21,7 +21,6 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
||||
import com.intellij.openapi.actionSystem.impl.Utils
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||
import com.intellij.openapi.components.Service
|
||||
@@ -35,6 +34,7 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.NativeAction
|
||||
import com.maddyhome.idea.vim.api.VimActionExecutor
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
||||
import com.maddyhome.idea.vim.newapi.IjNativeAction
|
||||
@@ -78,7 +78,6 @@ internal class IjActionExecutor : VimActionExecutor {
|
||||
val dataContext = DataContextWrapper(context.ij)
|
||||
dataContext.putUserData(runFromVimKey, true)
|
||||
|
||||
val actionId = ActionManager.getInstance().getId(ijAction)
|
||||
val event = AnActionEvent(
|
||||
null,
|
||||
dataContext,
|
||||
@@ -87,20 +86,11 @@ internal class IjActionExecutor : VimActionExecutor {
|
||||
ActionManager.getInstance(),
|
||||
0,
|
||||
)
|
||||
Utils.initUpdateSession(event)
|
||||
// beforeActionPerformedUpdate should be called to update the action. It fixes some rider-specific problems.
|
||||
// because rider use async update method. See VIM-1819.
|
||||
// This method executes inside of lastUpdateAndCheckDumb
|
||||
// Another related issue: VIM-2604
|
||||
|
||||
// This is a hack to fix the tests and fix VIM-3332
|
||||
// We should get rid of it in VIM-3376
|
||||
if (actionId == "RunClass" || actionId == IdeActions.ACTION_COMMENT_LINE || actionId == IdeActions.ACTION_COMMENT_BLOCK) {
|
||||
ijAction.beforeActionPerformedUpdate(event)
|
||||
if (!event.presentation.isEnabled) return false
|
||||
} else {
|
||||
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
|
||||
}
|
||||
if (!ActionUtil.lastUpdateAndCheckDumb(ijAction, event, false)) return false
|
||||
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
|
||||
// Some ActionGroups should not be performed, but shown as a popup
|
||||
val popup = JBPopupFactory.getInstance()
|
||||
@@ -224,7 +214,7 @@ internal class IjActionExecutor : VimActionExecutor {
|
||||
CommandProcessor.getInstance()
|
||||
.executeCommand(
|
||||
editor.ij.project,
|
||||
{ cmd.execute(editor, context, operatorArguments) },
|
||||
{ cmd.execute(editor, injector.executionContextManager.onEditor(editor, context), operatorArguments) },
|
||||
cmd.id,
|
||||
DocCommandGroupId.noneGroupId(editor.ij.document),
|
||||
UndoConfirmationPolicy.DEFAULT,
|
||||
|
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.VisualPosition
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
@@ -50,8 +51,8 @@ internal class IjEditorHelper : EngineEditorHelper {
|
||||
return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
|
||||
}
|
||||
|
||||
override fun pad(editor: VimEditor, line: Int, to: Int): String {
|
||||
return EditorHelper.pad(editor.ij, line, to)
|
||||
override fun pad(editor: VimEditor, context: ExecutionContext, line: Int, to: Int): String {
|
||||
return EditorHelper.pad(editor.ij, context.ij, line, to)
|
||||
}
|
||||
|
||||
override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
|
||||
|
@@ -34,15 +34,15 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
|
||||
val returnTo = this.vim.vimStateMachine.mode.returnTo
|
||||
when (returnTo) {
|
||||
ReturnTo.INSERT -> {
|
||||
this.vim.mode = Mode.INSERT
|
||||
this.vim.vimStateMachine.mode = Mode.INSERT
|
||||
}
|
||||
|
||||
ReturnTo.REPLACE -> {
|
||||
this.vim.mode = Mode.REPLACE
|
||||
this.vim.vimStateMachine.mode = Mode.REPLACE
|
||||
}
|
||||
|
||||
null -> {
|
||||
this.vim.mode = Mode.NORMAL()
|
||||
this.vim.vimStateMachine.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.mode = Mode.INSERT
|
||||
this.vimStateMachine.mode = Mode.INSERT
|
||||
}
|
||||
|
||||
ReturnTo.REPLACE -> {
|
||||
this.mode = Mode.REPLACE
|
||||
this.vimStateMachine.mode = Mode.REPLACE
|
||||
}
|
||||
|
||||
null -> {
|
||||
this.mode = Mode.NORMAL()
|
||||
this.vimStateMachine.mode = Mode.NORMAL()
|
||||
}
|
||||
}
|
||||
SelectionVimListenerSuppressor.lock().use {
|
||||
|
@@ -26,13 +26,15 @@ 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.*;
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult;
|
||||
import com.maddyhome.idea.vim.regexp.CharPointer;
|
||||
import com.maddyhome.idea.vim.regexp.RegExp;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparator;
|
||||
@@ -632,6 +634,113 @@ public class SearchHelper {
|
||||
return new TextRange(bstart, bend + 1);
|
||||
}
|
||||
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiComment comment,
|
||||
int pos,
|
||||
@Nullable String prefix,
|
||||
@Nullable String suffix) {
|
||||
if (prefix != null && suffix != null) {
|
||||
// TODO: Try to get rid of `getText()` because it takes a lot of time to calculate the string
|
||||
final String commentText = comment.getText();
|
||||
if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) {
|
||||
final int endOffset = comment.getTextOffset() + comment.getTextLength();
|
||||
if (pos < comment.getTextOffset() + prefix.length()) {
|
||||
return endOffset;
|
||||
}
|
||||
else if (pos >= endOffset - suffix.length()) {
|
||||
return comment.getTextOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiElement element, int pos) {
|
||||
final Language language = element.getLanguage();
|
||||
final Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(language);
|
||||
final PsiComment comment = PsiTreeUtil.getParentOfType(element, PsiComment.class, false);
|
||||
if (comment != null) {
|
||||
final int ret = findMatchingBlockCommentPair(comment, pos, commenter.getBlockCommentPrefix(),
|
||||
commenter.getBlockCommentSuffix());
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
if (commenter instanceof CodeDocumentationAwareCommenter docCommenter) {
|
||||
return findMatchingBlockCommentPair(comment, pos, docCommenter.getDocumentationCommentPrefix(),
|
||||
docCommenter.getDocumentationCommentSuffix());
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This looks on the current line, starting at the cursor position for one of {, }, (, ), [, or ]. It then searches
|
||||
* forward or backward, as appropriate for the associated match pair. String in double quotes are skipped over.
|
||||
* Single characters in single quotes are skipped too.
|
||||
*
|
||||
* @param editor The editor to search in
|
||||
* @return The offset within the editor of the found character or -1 if no match was found or none of the characters
|
||||
* were found on the remainder of the current line.
|
||||
*/
|
||||
public static int findMatchingPairOnCurrentLine(@NotNull Editor editor, @NotNull Caret caret) {
|
||||
int pos = caret.getOffset();
|
||||
|
||||
final int commentPos = findMatchingComment(editor, pos);
|
||||
if (commentPos >= 0) {
|
||||
return commentPos;
|
||||
}
|
||||
|
||||
int line = caret.getLogicalPosition().line;
|
||||
final IjVimEditor vimEditor = new IjVimEditor(editor);
|
||||
int end = EngineEditorHelperKt.getLineEndOffset(vimEditor, line, true);
|
||||
|
||||
// To handle the case where visual mode allows the user to go past the end of the line,
|
||||
// which will prevent loc from finding a pairable character below
|
||||
if (pos > 0 && pos == end) {
|
||||
pos = end - 1;
|
||||
}
|
||||
|
||||
final String pairChars = parseMatchPairsOption(vimEditor);
|
||||
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
int loc = -1;
|
||||
// Search the remainder of the current line for one of the candidate characters
|
||||
while (pos < end) {
|
||||
loc = pairChars.indexOf(chars.charAt(pos));
|
||||
if (loc >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
int res = -1;
|
||||
// If we found one ...
|
||||
if (loc >= 0) {
|
||||
// What direction should we go now (-1 is backward, 1 is forward)
|
||||
Direction dir = loc % 2 == 0 ? Direction.FORWARDS : Direction.BACKWARDS;
|
||||
// Which character did we find and which should we now search for
|
||||
char found = pairChars.charAt(loc);
|
||||
char match = pairChars.charAt(loc + dir.toInt());
|
||||
res = findBlockLocation(chars, found, match, dir, pos, 1, true);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* If on the start/end of a block comment, jump to the matching of that comment, or vice versa.
|
||||
*/
|
||||
private static int findMatchingComment(@NotNull Editor editor, int pos) {
|
||||
final PsiFile psiFile = PsiHelper.getFile(editor);
|
||||
if (psiFile != null) {
|
||||
final PsiElement element = psiFile.findElementAt(pos);
|
||||
if (element != null) {
|
||||
return findMatchingBlockCommentPair(element, pos);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int findBlockLocation(@NotNull CharSequence chars,
|
||||
char found,
|
||||
char match,
|
||||
@@ -1494,27 +1603,21 @@ public class SearchHelper {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
if (offsets.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (skipCount >= offsets.size()) {
|
||||
return offsets.lastInt();
|
||||
}
|
||||
else {
|
||||
IntIterator offsetIterator = offsets.iterator();
|
||||
skip(offsetIterator, skipCount);
|
||||
offsetIterator.skip(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,7 +26,6 @@ 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
|
||||
@@ -87,24 +86,11 @@ 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
|
||||
val editors = injector.editorGroup.getEditors(IjVimDocument(current.document))
|
||||
for (vimEditor in editors) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
if (editor.project != project) {
|
||||
continue
|
||||
}
|
||||
|
||||
// [VERSION UPDATE] 202+ Use editors
|
||||
val editors = localEditors(current.document, project)
|
||||
for (editor in editors) {
|
||||
// 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,8 +31,4 @@ 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()
|
||||
}
|
||||
|
@@ -10,13 +10,10 @@ package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.command.undo.UndoManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
@@ -43,11 +40,23 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
val scrollingModel = editor.getScrollingModel()
|
||||
scrollingModel.accumulateViewportChanges()
|
||||
|
||||
// [VERSION UPDATE] 241+ remove this if
|
||||
if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
|
||||
undoFor241plus(editor, undoManager, fileEditor)
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
|
||||
restoreVisualMode(editor)
|
||||
} else {
|
||||
undoForLessThan241(undoManager, fileEditor, editor)
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.undo(fileEditor)
|
||||
|
||||
// We execute undo one more time if the previous one just restored selection
|
||||
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
|
||||
undoManager.undo(fileEditor)
|
||||
}
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
|
||||
scrollingModel.flushViewportChanges()
|
||||
@@ -57,134 +66,43 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun undoForLessThan241(
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
editor: VimEditor,
|
||||
) {
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
|
||||
restoreVisualMode(editor)
|
||||
} else {
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.undo(fileEditor)
|
||||
restoreVisualMode(editor)
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun undoFor241plus(
|
||||
editor: VimEditor,
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
) {
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.undo(fileEditor)
|
||||
restoreVisualMode(editor)
|
||||
}
|
||||
} else {
|
||||
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
|
||||
undoManager.undo(fileEditor)
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasSelection(editor: VimEditor): Boolean {
|
||||
return editor.primaryCaret().ij.hasSelection()
|
||||
}
|
||||
|
||||
|
||||
override fun redo(editor: VimEditor, context: ExecutionContext): Boolean {
|
||||
val ijContext = context.context as DataContext
|
||||
val project = PlatformDataKeys.PROJECT.getData(ijContext) ?: return false
|
||||
val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor.ij)
|
||||
val undoManager = UndoManager.getInstance(project)
|
||||
if (undoManager.isRedoAvailable(fileEditor)) {
|
||||
// [VERSION UPDATE] 241+ remove this if
|
||||
if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
|
||||
redoFor241Plus(undoManager, fileEditor, editor)
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
||||
restoreVisualMode(editor)
|
||||
} else {
|
||||
redoForLessThan241(undoManager, fileEditor, editor)
|
||||
}
|
||||
undoManager.redo(fileEditor)
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
editor.carets().forEach { it.ij.removeSelection() }
|
||||
}
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.redo(fileEditor)
|
||||
|
||||
// We execute undo one more time if the previous one just restored selection
|
||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||
undoManager.redo(fileEditor)
|
||||
}
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun redoForLessThan241(
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
editor: VimEditor,
|
||||
) {
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
||||
} else {
|
||||
undoManager.redo(fileEditor)
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
editor.carets().forEach { it.ij.removeSelection() }
|
||||
}
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.redo(fileEditor)
|
||||
|
||||
// We execute undo one more time if the previous one just restored selection
|
||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||
undoManager.redo(fileEditor)
|
||||
}
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun redoFor241Plus(
|
||||
undoManager: UndoManager,
|
||||
fileEditor: TextEditor,
|
||||
editor: VimEditor,
|
||||
) {
|
||||
if (injector.globalIjOptions().oldundo) {
|
||||
undoManager.redo(fileEditor)
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
editor.carets().forEach { it.ij.removeSelection() }
|
||||
}
|
||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
||||
editor.runWithChangeTracking {
|
||||
undoManager.redo(fileEditor)
|
||||
|
||||
// We execute undo one more time if the previous one just restored selection
|
||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||
undoManager.redo(fileEditor)
|
||||
}
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
} else {
|
||||
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
|
||||
undoManager.redo(fileEditor)
|
||||
}
|
||||
|
||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||
removeSelections(editor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeSelections(editor: VimEditor) {
|
||||
editor.carets().forEach {
|
||||
val ijCaret = it.ij
|
||||
@@ -196,17 +114,6 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun runWithBooleanRegistryOption(option: String, value: Boolean, block: () -> Unit) {
|
||||
val registry = Registry.get(option)
|
||||
val oldValue = registry.asBoolean()
|
||||
registry.setValue(value)
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
registry.setValue(oldValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun VimEditor.runWithChangeTracking(block: ChangeTracker.() -> Unit) {
|
||||
val tracker = ChangeTracker(this)
|
||||
tracker.block()
|
||||
@@ -233,7 +140,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
private fun restoreVisualMode(editor: VimEditor) {
|
||||
if (!editor.inVisualMode && editor.getSelectionModel().hasSelection()) {
|
||||
val detectedMode = VimPlugin.getVisualMotion().autodetectVisualSubmode(editor)
|
||||
|
||||
|
||||
// Visual block selection is restored into multiple carets, so multi-carets that form a block are always
|
||||
// identified as visual block mode, leading to false positives.
|
||||
// Since I use visual block mode much less often than multi-carets, this is a judgment call to never restore
|
||||
@@ -242,7 +149,7 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
||||
SelectionType.CHARACTER_WISE
|
||||
else
|
||||
detectedMode
|
||||
|
||||
|
||||
VimPlugin.getVisualMotion().enterVisualMode(editor, wantedMode)
|
||||
}
|
||||
}
|
||||
|
@@ -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,8 +99,6 @@ 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
|
||||
@@ -108,7 +106,6 @@ 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.getClassLoader());
|
||||
return IconManager.getInstance().getIcon(path, VimIcons.class);
|
||||
}
|
||||
}
|
||||
|
@@ -9,19 +9,11 @@
|
||||
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 {
|
||||
@@ -31,54 +23,11 @@ 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
|
||||
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'"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.registerProblem(element, TextRange.create(10, 20), "Hi there")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.acejump.session.SessionManager
|
||||
|
||||
@@ -16,11 +16,12 @@ 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? = ApplicationManager.getApplication().getService(AceJumpService::class.java)
|
||||
fun getInstance(): AceJumpService? = ServiceManager.getService(AceJumpService::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,6 @@ import com.intellij.codeInsight.template.TemplateManagerListener
|
||||
import com.intellij.codeInsight.template.impl.TemplateState
|
||||
import com.intellij.find.FindModelListener
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.AnActionResult
|
||||
@@ -78,7 +77,7 @@ internal object IdeaSpecifics {
|
||||
}
|
||||
}
|
||||
|
||||
if (hostEditor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
|
||||
if (hostEditor != null && action is ChooseItemAction && hostEditor.vimStateMachine?.isRecording == true) {
|
||||
val lookup = LookupManager.getActiveLookup(hostEditor)
|
||||
if (lookup != null) {
|
||||
val charsToRemove = hostEditor.caretModel.primaryCaret.offset - lookup.lookupStart
|
||||
@@ -100,7 +99,7 @@ internal object IdeaSpecifics {
|
||||
|
||||
val editor = editor
|
||||
if (editor != null) {
|
||||
if (action is ChooseItemAction && injector.registerGroup.isRecording) {
|
||||
if (action is ChooseItemAction && editor.vimStateMachine?.isRecording == true) {
|
||||
val prevDocumentLength = completionPrevDocumentLength
|
||||
val prevDocumentOffset = completionPrevDocumentOffset
|
||||
|
||||
@@ -127,12 +126,10 @@ internal object IdeaSpecifics {
|
||||
)
|
||||
}
|
||||
) {
|
||||
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)
|
||||
}
|
||||
val commandState = editor.vim.vimStateMachine
|
||||
commandState.mode = Mode.NORMAL()
|
||||
VimPlugin.getChange().insertBeforeCursor(editor.vim, event.dataContext.vim)
|
||||
KeyHandler.getInstance().reset(editor.vim)
|
||||
}
|
||||
//endregion
|
||||
|
||||
@@ -182,7 +179,7 @@ internal object IdeaSpecifics {
|
||||
if (editor.vim.inNormalMode) {
|
||||
VimPlugin.getChange().insertBeforeCursor(
|
||||
editor.vim,
|
||||
injector.executionContextManager.getEditorExecutionContext(editor.vim),
|
||||
injector.executionContextManager.onEditor(editor.vim),
|
||||
)
|
||||
KeyHandler.getInstance().reset(editor.vim)
|
||||
}
|
||||
@@ -232,7 +229,5 @@ internal class FindActionIdAction : DumbAwareToggleAction() {
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||
}
|
||||
//endregion
|
||||
|
@@ -8,7 +8,6 @@
|
||||
|
||||
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
|
||||
@@ -21,8 +20,6 @@ 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
|
||||
@@ -35,7 +32,6 @@ import com.intellij.openapi.editor.ex.DocumentEx
|
||||
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
|
||||
import com.intellij.openapi.editor.ex.FocusChangeListener
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||
@@ -46,17 +42,13 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
|
||||
import com.intellij.openapi.fileEditor.impl.EditorComposite
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.rd.createLifetime
|
||||
import com.intellij.openapi.rd.createNestedDisposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.removeUserData
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.ExceptionUtil
|
||||
import com.jetbrains.rd.util.lifetime.Lifetime
|
||||
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
|
||||
@@ -70,17 +62,15 @@ 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
|
||||
@@ -88,23 +78,27 @@ 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
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.SwingUtilities
|
||||
@@ -134,7 +128,6 @@ 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() {
|
||||
@@ -142,30 +135,11 @@ 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))
|
||||
}
|
||||
|
||||
@@ -183,29 +157,23 @@ 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(Options.showmode, modeWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
|
||||
optionGroup.addGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
|
||||
modeWidgetOptionListener.onGlobalOptionChanged()
|
||||
macroWidgetOptionListener.onGlobalOptionChanged()
|
||||
|
||||
// Listen for and initialise new editors
|
||||
optionGroup.addEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
|
||||
|
||||
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance().onOffDisposable)
|
||||
val busConnection = ApplicationManager.getApplication().messageBus.connect(VimPlugin.getInstance().onOffDisposable)
|
||||
busConnection.subscribe(FileOpenedSyncListener.TOPIC, VimEditorFactoryListener)
|
||||
|
||||
// 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)
|
||||
EditorFactory.getInstance().eventMulticaster.addCaretListener(VimCaretListener, VimPlugin.getInstance().onOffDisposable)
|
||||
val eventMulticaster = EditorFactory.getInstance().eventMulticaster as? EditorEventMulticasterEx
|
||||
eventMulticaster?.addFocusChangeListener(VimFocusListener, VimPlugin.getInstance().onOffDisposable)
|
||||
}
|
||||
|
||||
fun disable() {
|
||||
@@ -216,8 +184,8 @@ internal object VimListenerManager {
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.relativenumber, EditorGroup.NumberChangeListener.INSTANCE)
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.scrolloff, ScrollGroup.ScrollOptionsChangeListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showcmd, ShowCmdOptionChangeListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showmode, modeWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(Options.showmode, macroWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, modeWidgetOptionListener)
|
||||
optionGroup.removeGlobalOptionChangeListener(IjOptions.showmodewidget, macroWidgetOptionListener)
|
||||
optionGroup.removeEffectiveOptionValueChangeListener(Options.guicursor, GuicursorChangeListener)
|
||||
}
|
||||
}
|
||||
@@ -246,9 +214,7 @@ 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
|
||||
// Make sure we get all editors, including uninitialised
|
||||
injector.editorGroup.getEditorsRaw().forEach { vimEditor ->
|
||||
val editor = vimEditor.ij
|
||||
localEditors().forEach { editor ->
|
||||
if (!initialisedEditors.contains(editor)) {
|
||||
add(editor, getOpeningEditor(editor)?.vim ?: injector.fallbackWindow, LocalOptionInitialisationScenario.NEW)
|
||||
}
|
||||
@@ -256,24 +222,18 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
injector.editorGroup.getEditors().forEach { editor ->
|
||||
remove(editor.ij)
|
||||
localEditors().forEach { editor ->
|
||||
remove(editor, false)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
val pluginLifetime = VimPlugin.getInstance().createLifetime()
|
||||
val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
|
||||
val disposable =
|
||||
Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
|
||||
// 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
|
||||
val disposable = editor.project?.vimDisposable ?: return
|
||||
|
||||
val listenersDisposable = Disposer.newDisposable(disposable)
|
||||
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
||||
editor.putUserData(editorListenersDisposable, listenersDisposable)
|
||||
|
||||
Disposer.register(listenersDisposable) {
|
||||
if (VimListenerTestObject.enabled) {
|
||||
@@ -296,75 +256,60 @@ 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.getEditor().editorDeinit(editor, true)
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(editor: Editor) {
|
||||
val editorDisposable = editor.removeUserData(editorListenersDisposableKey)
|
||||
fun remove(editor: Editor, isReleased: Boolean) {
|
||||
val editorDisposable = editor.getUserData(editorListenersDisposable)
|
||||
if (editorDisposable != null) {
|
||||
Disposer.dispose(editorDisposable)
|
||||
}
|
||||
else {
|
||||
// We definitely do not expect this to happen
|
||||
StrictMode.fail("Editor doesn't have disposable attached. $editor")
|
||||
}
|
||||
else StrictMode.fail("Editor doesn't have disposable attached. $editor")
|
||||
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
val editorListenersDisposable = Key.create<Disposable>("IdeaVim listeners disposable")
|
||||
|
||||
object VimCaretListener : CaretListener {
|
||||
override fun caretAdded(event: CaretEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
}
|
||||
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
VimMarkServiceImpl.MarkUpdater.documentChanged(event)
|
||||
SearchGroup.DocumentSearchListener.INSTANCE.documentChanged(event)
|
||||
override fun caretRemoved(event: CaretEvent) {
|
||||
if (vimDisabled(event.editor)) return
|
||||
event.editor.updateCaretsVisualAttributes()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// We can't rely on being passed a non-null editor, so check for Code With Me scenarios explicitly
|
||||
if (VimPlugin.isNotEnabled() || !ClientId.isCurrentlyUnderLocalId) return
|
||||
if (VimPlugin.isNotEnabled()) return
|
||||
|
||||
val newEditor = event.newEditor
|
||||
if (newEditor is TextEditor) {
|
||||
val editor = newEditor.editor
|
||||
if (editor.isInsertMode) {
|
||||
editor.vim.mode = Mode.NORMAL()
|
||||
VimStateMachine.getInstance(editor).mode = Mode.NORMAL()
|
||||
KeyHandler.getInstance().reset(editor.vim)
|
||||
}
|
||||
}
|
||||
@@ -376,10 +321,6 @@ 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,
|
||||
@@ -391,8 +332,6 @@ 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
|
||||
@@ -413,7 +352,7 @@ internal object VimListenerManager {
|
||||
openingEditor == null -> LocalOptionInitialisationScenario.EDIT
|
||||
else -> LocalOptionInitialisationScenario.NEW
|
||||
}
|
||||
EditorListeners.add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
|
||||
add(event.editor, openingEditor?.vim ?: injector.fallbackWindow, scenario)
|
||||
firstEditorInitialised = true
|
||||
}
|
||||
else {
|
||||
@@ -442,7 +381,6 @@ 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)
|
||||
@@ -466,8 +404,6 @@ 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
|
||||
@@ -488,7 +424,7 @@ internal object VimListenerManager {
|
||||
(openingEditor.canBeReused || openingEditor.isPreview) && isInSameSplit && openingEditorIsClosed -> LocalOptionInitialisationScenario.EDIT
|
||||
else -> LocalOptionInitialisationScenario.NEW
|
||||
}
|
||||
EditorListeners.add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
|
||||
add(editor, openingEditor?.editor?.vim ?: injector.fallbackWindow, scenario)
|
||||
firstEditorInitialised = true
|
||||
}
|
||||
}
|
||||
@@ -503,16 +439,12 @@ 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
|
||||
@@ -532,22 +464,20 @@ internal object VimListenerManager {
|
||||
val startOffset = selectionEvent.newRange.startOffset
|
||||
val endOffset = selectionEvent.newRange.endOffset
|
||||
|
||||
// TODO: It is very confusing that this logic is split between EditorSelectionHandler and EditorMouseHandler
|
||||
if (MouseEventsDataHolder.dragEventCount < MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
&& lineStart != lineEnd && startOffset == caretOffset) {
|
||||
if (skipNDragEvents < skipEvents && 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
|
||||
caret.setSelection(
|
||||
ijVimEditor.coerceOffset(endOffset + 1),
|
||||
ijVimEditor.coerceOffset(startOffset),
|
||||
ijVimEditor.coerceOffset(endOffset + 1).point,
|
||||
ijVimEditor.coerceOffset(startOffset).point,
|
||||
)
|
||||
} else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
|
||||
// When dragging left from EOL on a non-empty line, the selection
|
||||
// should include the last character on the line
|
||||
caret.setSelection(
|
||||
ijVimEditor.coerceOffset(lineEnd),
|
||||
ijVimEditor.coerceOffset(lineEnd - 1),
|
||||
ijVimEditor.coerceOffset(lineEnd).point,
|
||||
ijVimEditor.coerceOffset(lineEnd - 1).point,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -564,24 +494,13 @@ 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)
|
||||
@@ -633,7 +552,7 @@ internal object VimListenerManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseEventsDataHolder.dragEventCount -= 1
|
||||
skipNDragEvents -= 1
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -648,7 +567,7 @@ internal object VimListenerManager {
|
||||
* (Both with mouse and with v$. IdeaVim treats v$ as an exclusive selection)
|
||||
*/
|
||||
private fun clearFirstSelectionEvents(e: EditorMouseEvent) {
|
||||
if (MouseEventsDataHolder.dragEventCount > 0) {
|
||||
if (skipNDragEvents > 0) {
|
||||
logger.debug("Mouse dragging")
|
||||
VimVisualTimer.swingTimer?.stop()
|
||||
if (!mouseDragging) {
|
||||
@@ -674,7 +593,9 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun mousePressed(event: EditorMouseEvent) {
|
||||
MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
|
||||
skipNDragEvents = skipEvents
|
||||
SelectionVimListenerSuppressor.reset()
|
||||
}
|
||||
|
||||
@@ -685,10 +606,12 @@ internal object VimListenerManager {
|
||||
* - Click-hold and switch editor (ctrl-tab)
|
||||
*/
|
||||
override fun mouseReleased(event: EditorMouseEvent) {
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
|
||||
SelectionVimListenerSuppressor.unlock()
|
||||
|
||||
clearFirstSelectionEvents(event)
|
||||
MouseEventsDataHolder.dragEventCount = MouseEventsDataHolder.allowedSkippedDragEvents
|
||||
skipNDragEvents = skipEvents
|
||||
if (mouseDragging) {
|
||||
logger.debug("Release mouse after dragging")
|
||||
val editor = event.editor
|
||||
@@ -708,6 +631,7 @@ internal object VimListenerManager {
|
||||
}
|
||||
|
||||
override fun mouseClicked(event: EditorMouseEvent) {
|
||||
if (event.editor.isIdeaVimDisabledHere) return
|
||||
logger.debug("Mouse clicked")
|
||||
|
||||
if (event.area == EditorMouseEventArea.EDITING_AREA) {
|
||||
@@ -753,21 +677,13 @@ 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 -> {
|
||||
@@ -804,22 +720,10 @@ 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 {
|
||||
@@ -834,6 +738,6 @@ internal object VimListenerTestObject {
|
||||
}
|
||||
|
||||
private object MouseEventsDataHolder {
|
||||
const val allowedSkippedDragEvents = 3
|
||||
var dragEventCount = allowedSkippedDragEvents
|
||||
const val skipEvents = 3
|
||||
var skipNDragEvents = skipEvents
|
||||
}
|
||||
|
@@ -12,8 +12,16 @@ import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
|
||||
internal open class IjEditorExecutionContext(override val context: DataContext) : ExecutionContext
|
||||
internal open class IjEditorExecutionContext(override val context: DataContext) : ExecutionContext.Editor {
|
||||
override fun updateEditor(editor: VimEditor): ExecutionContext {
|
||||
return IjEditorExecutionContext(injector.executionContextManager.onEditor(editor, context.vim).ij)
|
||||
}
|
||||
}
|
||||
|
||||
internal class IjCaretAndEditorExecutionContext(override val context: DataContext) : IjEditorExecutionContext(context), ExecutionContext.CaretAndEditor
|
||||
|
||||
// This key is stored in data context when the action is started from vim
|
||||
internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
|
||||
|
@@ -9,15 +9,23 @@
|
||||
package com.maddyhome.idea.vim.newapi
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ExecutionContextManagerBase
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||
|
||||
@Service
|
||||
internal class IjExecutionContextManager : ExecutionContextManagerBase() {
|
||||
override fun getEditorExecutionContext(editor: VimEditor): ExecutionContext {
|
||||
return EditorUtil.getEditorDataContext(editor.ij).vim
|
||||
override fun onEditor(editor: VimEditor, prevContext: ExecutionContext?): ExecutionContext.Editor {
|
||||
if (prevContext is ExecutionContext.CaretAndEditor) {
|
||||
return prevContext
|
||||
}
|
||||
return IjEditorExecutionContext(EditorDataContext.init((editor as IjVimEditor).editor, prevContext?.ij))
|
||||
}
|
||||
|
||||
override fun onCaret(caret: VimCaret, prevContext: ExecutionContext.Editor): ExecutionContext.CaretAndEditor {
|
||||
return IjCaretAndEditorExecutionContext(CaretSpecificDataContext.create(prevContext.ij, caret.ij))
|
||||
}
|
||||
}
|
||||
|
@@ -10,10 +10,12 @@ package com.maddyhome.idea.vim.newapi
|
||||
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.maddyhome.idea.vim.common.LiveRange
|
||||
import com.maddyhome.idea.vim.common.Offset
|
||||
import com.maddyhome.idea.vim.common.offset
|
||||
|
||||
internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
|
||||
override val startOffset: Int
|
||||
get() = marker.startOffset
|
||||
override val startOffset: Offset
|
||||
get() = marker.startOffset.offset
|
||||
}
|
||||
|
||||
public val RangeMarker.vim: LiveRange
|
||||
|
@@ -34,8 +34,4 @@ internal class IjNativeActionManager : NativeActionManager {
|
||||
public val AnAction.vim: IjNativeAction
|
||||
get() = IjNativeAction(this)
|
||||
|
||||
public class IjNativeAction(override val action: AnAction) : NativeAction {
|
||||
override fun toString(): String {
|
||||
return "IjNativeAction(action=$action)"
|
||||
}
|
||||
}
|
||||
public class IjNativeAction(override val action: AnAction) : NativeAction
|
||||
|
@@ -43,10 +43,6 @@ 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)
|
||||
@@ -58,6 +54,10 @@ 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,12 +82,6 @@ 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,6 +11,7 @@ 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
|
||||
@@ -21,7 +22,10 @@ import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimCaretBase
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||
import com.maddyhome.idea.vim.common.EditorLine
|
||||
import com.maddyhome.idea.vim.common.LiveRange
|
||||
import com.maddyhome.idea.vim.common.Offset
|
||||
import com.maddyhome.idea.vim.common.offset
|
||||
import com.maddyhome.idea.vim.group.visual.VisualChange
|
||||
import com.maddyhome.idea.vim.helper.lastSelectionInfo
|
||||
import com.maddyhome.idea.vim.helper.markStorage
|
||||
@@ -38,6 +42,14 @@ 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
|
||||
@@ -75,8 +87,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
||||
}
|
||||
override val editor: VimEditor
|
||||
get() = IjVimEditor(caret.editor)
|
||||
override val offset: Int
|
||||
get() = caret.offset
|
||||
override val offset: Offset
|
||||
get() = caret.offset.offset
|
||||
override var vimLastColumn: Int
|
||||
get() = caret.vimLastColumn
|
||||
set(value) {
|
||||
@@ -115,8 +127,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
||||
this.caret.moveToLogicalPosition(LogicalPosition(position.line, position.column, position.leansForward))
|
||||
}
|
||||
|
||||
override fun getLine(): Int {
|
||||
return caret.logicalPosition.line
|
||||
override fun getLine(): EditorLine.Pointer {
|
||||
return EditorLine.Pointer.init(caret.logicalPosition.line, editor)
|
||||
}
|
||||
|
||||
override fun hasSelection(): Boolean {
|
||||
@@ -161,8 +173,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setSelection(start: Int, end: Int) {
|
||||
caret.setSelection(start, end)
|
||||
override fun setSelection(start: Offset, end: Offset) {
|
||||
caret.setSelection(start.point, end.point)
|
||||
}
|
||||
|
||||
override fun removeSelection() {
|
||||
|
@@ -14,6 +14,7 @@ import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.maddyhome.idea.vim.api.VimDocument
|
||||
import com.maddyhome.idea.vim.common.ChangesListener
|
||||
import com.maddyhome.idea.vim.common.LiveRange
|
||||
import com.maddyhome.idea.vim.common.Offset
|
||||
|
||||
internal class IjVimDocument(val document: Document) : VimDocument {
|
||||
|
||||
@@ -40,7 +41,7 @@ internal class IjVimDocument(val document: Document) : VimDocument {
|
||||
document.removeDocumentListener(nativeListener)
|
||||
}
|
||||
|
||||
override fun getOffsetGuard(offset: Int): LiveRange? {
|
||||
return document.getOffsetGuard(offset)?.vim
|
||||
override fun getOffsetGuard(offset: Offset): LiveRange? {
|
||||
return document.getOffsetGuard(offset.point)?.vim
|
||||
}
|
||||
}
|
||||
|
@@ -35,11 +35,13 @@ import com.maddyhome.idea.vim.api.VimScrollingModel
|
||||
import com.maddyhome.idea.vim.api.VimSelectionModel
|
||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||
import com.maddyhome.idea.vim.api.VirtualFile
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.EditorLine
|
||||
import com.maddyhome.idea.vim.common.IndentConfig
|
||||
import com.maddyhome.idea.vim.common.LiveRange
|
||||
import com.maddyhome.idea.vim.common.Offset
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.offset
|
||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.StrictMode
|
||||
@@ -49,6 +51,8 @@ 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
|
||||
@@ -88,18 +92,18 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
return editor.document.lineCount
|
||||
}
|
||||
|
||||
override fun deleteRange(leftOffset: Int, rightOffset: Int) {
|
||||
editor.document.deleteString(leftOffset, rightOffset)
|
||||
override fun deleteRange(leftOffset: Offset, rightOffset: Offset) {
|
||||
editor.document.deleteString(leftOffset.point, rightOffset.point)
|
||||
}
|
||||
|
||||
override fun addLine(atPosition: Int): Int {
|
||||
val offset: Int = if (atPosition < lineCount()) {
|
||||
override fun addLine(atPosition: EditorLine.Offset): EditorLine.Pointer {
|
||||
val offset: Int = if (atPosition.line < lineCount()) {
|
||||
// The new line character is inserted before the new line char of the previous line. So it works line an enter
|
||||
// on a line end. I believe that the correct implementation would be to insert the new line char after the
|
||||
// \n of the previous line, however at the moment this won't update the mark on this line.
|
||||
// https://youtrack.jetbrains.com/issue/IDEA-286587
|
||||
|
||||
val lineStart = (editor.document.getLineStartOffset(atPosition) - 1).coerceAtLeast(0)
|
||||
val lineStart = (editor.document.getLineStartOffset(atPosition.line) - 1).coerceAtLeast(0)
|
||||
val guard = editor.document.getOffsetGuard(lineStart)
|
||||
if (guard != null && guard.endOffset == lineStart + 1) {
|
||||
// Dancing around guarded blocks. It may happen that this concrete position is locked, but the next
|
||||
@@ -114,11 +118,11 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
fileSize().toInt()
|
||||
}
|
||||
editor.document.insertString(offset, "\n")
|
||||
return atPosition
|
||||
return EditorLine.Pointer.init(atPosition.line, this)
|
||||
}
|
||||
|
||||
override fun insertText(atPosition: Int, text: CharSequence) {
|
||||
editor.document.insertString(atPosition, text)
|
||||
override fun insertText(atPosition: Offset, text: CharSequence) {
|
||||
editor.document.insertString(atPosition.point, text)
|
||||
}
|
||||
|
||||
override fun replaceString(start: Int, end: Int, newString: String) {
|
||||
@@ -126,13 +130,13 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
}
|
||||
|
||||
// TODO: 30.12.2021 Is end offset inclusive?
|
||||
override fun getLineRange(line: Int): Pair<Int, Int> {
|
||||
override fun getLineRange(line: EditorLine.Pointer): Pair<Offset, Offset> {
|
||||
// TODO: 30.12.2021 getLineEndOffset returns the same value for "xyz" and "xyz\n"
|
||||
return editor.document.getLineStartOffset(line) to editor.document.getLineEndOffset(line)
|
||||
return editor.document.getLineStartOffset(line.line).offset to editor.document.getLineEndOffset(line.line).offset
|
||||
}
|
||||
|
||||
override fun getLine(offset: Int): Int {
|
||||
return editor.offsetToLogicalPosition(offset).line
|
||||
override fun getLine(offset: Offset): EditorLine.Pointer {
|
||||
return EditorLine.Pointer.init(editor.offsetToLogicalPosition(offset.point).line, this)
|
||||
}
|
||||
|
||||
override fun carets(): List<VimCaret> {
|
||||
@@ -201,15 +205,15 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
return editor.isOneLineMode
|
||||
}
|
||||
|
||||
override fun getText(left: Int, right: Int): CharSequence {
|
||||
return editor.document.charsSequence.subSequence(left, right)
|
||||
override fun getText(left: Offset, right: Offset): CharSequence {
|
||||
return editor.document.charsSequence.subSequence(left.point, right.point)
|
||||
}
|
||||
|
||||
override fun search(
|
||||
pair: Pair<Int, Int>,
|
||||
pair: Pair<Offset, Offset>,
|
||||
editor: VimEditor,
|
||||
shiftType: LineDeleteShift,
|
||||
): Pair<Pair<Int, Int>, LineDeleteShift>? {
|
||||
): Pair<Pair<Offset, Offset>, LineDeleteShift>? {
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
return when (shiftType) {
|
||||
LineDeleteShift.NO_NL -> if (pair.noGuard(ijEditor)) return pair to shiftType else null
|
||||
@@ -239,6 +243,14 @@ 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) }
|
||||
}
|
||||
@@ -356,10 +368,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
return EditorHelper.getVirtualFile(editor)?.getUrl()?.let { VirtualFileManager.extractProtocol(it) }
|
||||
}
|
||||
|
||||
override val projectId = editor.project?.let { injector.file.getProjectId(it) } ?: DEFAULT_PROJECT_ID
|
||||
override val projectId = editor.project?.basePath ?: DEFAULT_PROJECT_ID
|
||||
|
||||
override fun visualPositionToOffset(position: VimVisualPosition): Int {
|
||||
return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight))
|
||||
override fun visualPositionToOffset(position: VimVisualPosition): Offset {
|
||||
return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight)).offset
|
||||
}
|
||||
|
||||
override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
@@ -415,8 +427,8 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
return visualPosition.run { VimVisualPosition(line, column, leansRight) }
|
||||
}
|
||||
|
||||
override fun createLiveMarker(start: Int, end: Int): LiveRange {
|
||||
return editor.document.createRangeMarker(start, end).vim
|
||||
override fun createLiveMarker(start: Offset, end: Offset): LiveRange {
|
||||
return editor.document.createRangeMarker(start.point, end.point).vim
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,10 +466,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
ijFoldRegion.isExpanded = value
|
||||
}
|
||||
}
|
||||
override val startOffset: Int
|
||||
get() = ijFoldRegion.startOffset
|
||||
override val endOffset: Int
|
||||
get() = ijFoldRegion.endOffset
|
||||
override val startOffset: Offset
|
||||
get() = Offset(ijFoldRegion.startOffset)
|
||||
override val endOffset: Offset
|
||||
get() = Offset(ijFoldRegion.endOffset)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -466,17 +478,17 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
||||
return caret
|
||||
}
|
||||
|
||||
private fun Pair<Int, Int>.noGuard(editor: Editor): Boolean {
|
||||
return editor.document.getRangeGuard(this.first, this.second) == null
|
||||
private fun Pair<Offset, Offset>.noGuard(editor: Editor): Boolean {
|
||||
return editor.document.getRangeGuard(this.first.point, this.second.point) == null
|
||||
}
|
||||
|
||||
private inline fun Pair<Int, Int>.shift(
|
||||
private inline fun Pair<Offset, Offset>.shift(
|
||||
shiftStart: Int = 0,
|
||||
shiftEnd: Int = 0,
|
||||
action: Pair<Int, Int>.() -> Unit,
|
||||
action: Pair<Offset, Offset>.() -> Unit,
|
||||
) {
|
||||
val data =
|
||||
(this.first + shiftStart).coerceAtLeast(0) to (this.second + shiftEnd).coerceAtLeast(0)
|
||||
(this.first.point + shiftStart).coerceAtLeast(0).offset to (this.second.point + shiftEnd).coerceAtLeast(0).offset
|
||||
data.action()
|
||||
}
|
||||
|
||||
@@ -498,6 +510,3 @@ public val Editor.vim: VimEditor
|
||||
get() = IjVimEditor(this)
|
||||
public val VimEditor.ij: Editor
|
||||
get() = (this as IjVimEditor).editor
|
||||
|
||||
public val com.intellij.openapi.util.TextRange.vim: TextRange
|
||||
get() = TextRange(this.startOffset, this.endOffset)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user