mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2024-11-25 07:42:59 +01:00
Compare commits
69 Commits
a978505530
...
063ed0aa84
Author | SHA1 | Date | |
---|---|---|---|
063ed0aa84 | |||
fccfe0c2ea | |||
45ed79d865 | |||
527eb4cbb3 | |||
e32ac125b2 | |||
4d1d3b697c | |||
3246832528 | |||
6505bfc9aa | |||
0c63890e9d | |||
259958e702 | |||
4916545b53 | |||
b8a9bddfa9 | |||
95688b33c8 | |||
07f44f1c93 | |||
2ce6239ad6 | |||
a0d2d64237 | |||
2e4e8c058b | |||
f464d25844 | |||
acc12c5b17 | |||
0c1bbd5e92 | |||
f330e220ad | |||
|
b86ec03dc4 | ||
|
ae75498f8a | ||
|
9d0b68b0f8 | ||
|
eeb5939e59 | ||
|
ef235a47bf | ||
|
b66da76880 | ||
|
54d6119784 | ||
|
0b8c081425 | ||
|
209052ffa6 | ||
|
fe9a6b5cfb | ||
|
9c0f74369f | ||
|
cd27e5229b | ||
|
472732905c | ||
|
485d9f81cd | ||
|
8cf136ce4c | ||
|
116a8ac9d2 | ||
|
fda310bda6 | ||
|
e55619ea33 | ||
|
b952b20128 | ||
|
62d1f85648 | ||
|
5e3c8c0e92 | ||
|
b58dddf2ff | ||
|
78d351a0b0 | ||
|
61dbc948cc | ||
|
c4d92ebe73 | ||
|
d0cf827638 | ||
|
6a6a92b6b9 | ||
|
9869b8a34e | ||
|
60fbf88322 | ||
|
fae3924062 | ||
|
dc2ce64823 | ||
|
d0d86d9178 | ||
|
f417af6148 | ||
|
2fe2860a09 | ||
|
cb40426976 | ||
|
423ed390a2 | ||
|
7652b16ca6 | ||
|
618a010c15 | ||
|
d44a34ed9b | ||
|
c84fc996db | ||
|
43f232543b | ||
|
3f65d1d99a | ||
|
bfcf706ca7 | ||
|
8c1103c461 | ||
|
ab75ace8db | ||
|
4a58e6a282 | ||
|
ac9e4f69b4 | ||
|
581edba7fd |
6
.github/workflows/runUiOctopusTests.yml
vendored
6
.github/workflows/runUiOctopusTests.yml
vendored
@ -15,11 +15,7 @@ jobs:
|
|||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
run: brew install ffmpeg
|
||||||
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
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
|
6
.github/workflows/runUiPyTests.yml
vendored
6
.github/workflows/runUiPyTests.yml
vendored
@ -18,11 +18,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
run: brew install ffmpeg
|
||||||
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
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
|
6
.github/workflows/runUiTests.yml
vendored
6
.github/workflows/runUiTests.yml
vendored
@ -15,11 +15,7 @@ jobs:
|
|||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
run: brew install ffmpeg
|
||||||
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
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
|
34
.github/workflows/updateAffectedRate.yml
vendored
34
.github/workflows/updateAffectedRate.yml
vendored
@ -1,34 +0,0 @@
|
|||||||
# 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 }}
|
|
1
.teamcity/_Self/Project.kt
vendored
1
.teamcity/_Self/Project.kt
vendored
@ -25,6 +25,7 @@ object Project : Project({
|
|||||||
// Active tests
|
// Active tests
|
||||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
||||||
|
buildType(TestingBuildType("2024.1", "<default>"))
|
||||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
|
|
||||||
buildType(PropertyBased)
|
buildType(PropertyBased)
|
||||||
|
@ -21,7 +21,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.19")
|
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
||||||
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
@ -51,11 +51,11 @@ buildscript {
|
|||||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||||
classpath("org.kohsuke:github-api:1.305")
|
classpath("org.kohsuke:github-api:1.305")
|
||||||
|
|
||||||
classpath("io.ktor:ktor-client-core:2.3.9")
|
classpath("io.ktor:ktor-client-core:2.3.10")
|
||||||
classpath("io.ktor:ktor-client-cio:2.3.9")
|
classpath("io.ktor:ktor-client-cio:2.3.10")
|
||||||
classpath("io.ktor:ktor-client-auth:2.3.9")
|
classpath("io.ktor:ktor-client-auth:2.3.10")
|
||||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.9")
|
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
|
||||||
|
|
||||||
// This comes from the changelog plugin
|
// This comes from the changelog plugin
|
||||||
// classpath("org.jetbrains:markdown:0.3.1")
|
// classpath("org.jetbrains:markdown:0.3.1")
|
||||||
@ -69,11 +69,11 @@ plugins {
|
|||||||
application
|
application
|
||||||
id("java-test-fixtures")
|
id("java-test-fixtures")
|
||||||
|
|
||||||
id("org.jetbrains.intellij") version "1.17.2"
|
id("org.jetbrains.intellij") version "1.17.3"
|
||||||
id("org.jetbrains.changelog") version "2.2.0"
|
id("org.jetbrains.changelog") version "2.2.0"
|
||||||
|
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||||
id("com.dorongold.task-tree") version "2.1.1"
|
id("com.dorongold.task-tree") version "3.0.0"
|
||||||
|
|
||||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ dependencies {
|
|||||||
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
|
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
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-engine:5.10.2")
|
||||||
@ -264,9 +264,6 @@ tasks {
|
|||||||
runPluginVerifier {
|
runPluginVerifier {
|
||||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
||||||
teamCityOutputFormat.set(true)
|
teamCityOutputFormat.set(true)
|
||||||
|
|
||||||
// The latest version of the plugin verifier is broken, so temporally use the stable version
|
|
||||||
verifierVersion = "1.307"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateGrammarSource {
|
generateGrammarSource {
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
|
|
||||||
#ideaVersion=LATEST-EAP-SNAPSHOT
|
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||||
ideaVersion=2023.3.3
|
ideaVersion=2024.1
|
||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||||
ideaType=IC
|
ideaType=IC
|
||||||
downloadIdeaSources=true
|
downloadIdeaSources=true
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=chylex-31
|
version=chylex-32
|
||||||
javaVersion=17
|
javaVersion=17
|
||||||
remoteRobotVersion=0.11.22
|
remoteRobotVersion=0.11.22
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
@ -42,4 +42,3 @@ kotlin.stdlib.default.dependency=false
|
|||||||
|
|
||||||
# Disable incremental annotation processing
|
# Disable incremental annotation processing
|
||||||
ksp.incremental=false
|
ksp.incremental=false
|
||||||
|
|
||||||
|
@ -22,11 +22,11 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core:2.3.9")
|
implementation("io.ktor:ktor-client-core:2.3.10")
|
||||||
implementation("io.ktor:ktor-client-cio:2.3.9")
|
implementation("io.ktor:ktor-client-cio:2.3.10")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.9")
|
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.10")
|
||||||
implementation("io.ktor:ktor-client-auth:2.3.9")
|
implementation("io.ktor:ktor-client-auth:2.3.10")
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||||
|
|
||||||
// This is needed for jgit to connect to ssh
|
// This is needed for jgit to connect to ssh
|
||||||
@ -58,13 +58,6 @@ tasks.register("checkNewPluginDependencies", JavaExec::class) {
|
|||||||
classpath = sourceSets["main"].runtimeClasspath
|
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) {
|
tasks.register("calculateNewVersion", JavaExec::class) {
|
||||||
group = "release"
|
group = "release"
|
||||||
mainClass.set("scripts.release.CalculateNewVersionKt")
|
mainClass.set("scripts.release.CalculateNewVersionKt")
|
||||||
|
@ -49,17 +49,13 @@ suspend fun main() {
|
|||||||
}
|
}
|
||||||
val output = response.body<List<String>>().toSet()
|
val output = response.body<List<String>>().toSet()
|
||||||
println(output)
|
println(output)
|
||||||
if (knownPlugins != output) {
|
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||||
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") }
|
// val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||||
error(
|
error(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Unregistered plugins:
|
Unregistered plugins:
|
||||||
${if (newPlugins.isNotEmpty()) newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No unregistered plugins"}
|
${newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" }}
|
||||||
|
|
||||||
Removed plugins:
|
|
||||||
${if (removedPlugins.isNotEmpty()) removedPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No removed plugins"}
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package 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()
|
|
||||||
}
|
|
@ -77,7 +77,7 @@ public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActio
|
|||||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||||
val startTime = if (traceTime) System.currentTimeMillis() else null
|
val startTime = if (traceTime) System.currentTimeMillis() else null
|
||||||
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim), handler.keyHandlerState)
|
handler.handleKey(editor.vim, keyStroke, context.vim, handler.keyHandlerState)
|
||||||
if (startTime != null) {
|
if (startTime != null) {
|
||||||
val duration = System.currentTimeMillis() - startTime
|
val duration = System.currentTimeMillis() - startTime
|
||||||
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
||||||
|
@ -79,12 +79,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
|||||||
try {
|
try {
|
||||||
val start = if (traceTime) System.currentTimeMillis() else null
|
val start = if (traceTime) System.currentTimeMillis() else null
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
keyHandler.handleKey(
|
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
||||||
editor.vim,
|
|
||||||
keyStroke,
|
|
||||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
|
||||||
keyHandler.keyHandlerState,
|
|
||||||
)
|
|
||||||
if (start != null) {
|
if (start != null) {
|
||||||
val duration = System.currentTimeMillis() - start
|
val duration = System.currentTimeMillis() - start
|
||||||
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
||||||
@ -381,6 +376,10 @@ private class ActionEnableStatus(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "ActionEnableStatus(isEnabled=$isEnabled, message='$message', logLevel=$logLevel)"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG = logger<ActionEnableStatus>()
|
private val LOG = logger<ActionEnableStatus>()
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecu
|
|||||||
}
|
}
|
||||||
injector.editorGroup.notifyIdeaJoin(editor)
|
injector.editorGroup.notifyIdeaJoin(editor)
|
||||||
var res = true
|
var res = true
|
||||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||||
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
||||||
res = false
|
res = false
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExe
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var res = true
|
var res = true
|
||||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||||
if (!caret.isValid) return@forEach
|
if (!caret.isValid) return@forEach
|
||||||
val range = caretsAndSelections[caret] ?: return@forEach
|
val range = caretsAndSelections[caret] ?: return@forEach
|
||||||
if (!injector.changeGroup.deleteJoinRange(
|
if (!injector.changeGroup.deleteJoinRange(
|
||||||
|
@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.Sin
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var res = true
|
var res = true
|
||||||
editor.carets().sortedByDescending { it.offset.point }.forEach { caret ->
|
editor.carets().sortedByDescending { it.offset }.forEach { caret ->
|
||||||
if (!caret.isValid) return@forEach
|
if (!caret.isValid) return@forEach
|
||||||
val range = caretsAndSelections[caret] ?: return@forEach
|
val range = caretsAndSelections[caret] ?: return@forEach
|
||||||
if (!injector.changeGroup.deleteJoinRange(
|
if (!injector.changeGroup.deleteJoinRange(
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
package com.maddyhome.idea.vim.common
|
package com.maddyhome.idea.vim.common
|
||||||
|
|
||||||
import com.intellij.application.options.CodeStyle
|
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.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
||||||
@ -39,13 +37,12 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(editor: Editor, context: DataContext): IndentConfig {
|
fun create(editor: Editor): IndentConfig {
|
||||||
return create(editor, PlatformDataKeys.PROJECT.getData(context))
|
return create(editor, editor.project)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@JvmOverloads
|
fun create(editor: Editor, project: Project?): IndentConfig {
|
||||||
fun create(editor: Editor, project: Project? = editor.project): IndentConfig {
|
|
||||||
val indentOptions = if (project != null) {
|
val indentOptions = if (project != null) {
|
||||||
CodeStyle.getIndentOptions(project, editor.document)
|
CodeStyle.getIndentOptions(project, editor.document)
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.extension
|
package com.maddyhome.idea.vim.extension
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
@ -142,7 +143,7 @@ public object VimExtensionFacade {
|
|||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||||
val context = injector.executionContextManager.onEditor(editor.vim)
|
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
||||||
}
|
}
|
||||||
@ -181,8 +182,8 @@ public object VimExtensionFacade {
|
|||||||
|
|
||||||
/** Returns a string typed in the input box similar to 'input()'. */
|
/** Returns a string typed in the input box similar to 'input()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun inputString(editor: Editor, prompt: String, finishOn: Char?): String {
|
public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||||
return service<CommandLineHelper>().inputString(editor.vim, prompt, finishOn) ?: ""
|
return service<CommandLineHelper>().inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||||
|
@ -8,25 +8,21 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.extension.nerdtree
|
package com.maddyhome.idea.vim.extension.nerdtree
|
||||||
|
|
||||||
import com.intellij.ide.projectView.ProjectView
|
|
||||||
import com.intellij.ide.projectView.impl.AbstractProjectViewPane
|
|
||||||
import com.intellij.ide.projectView.impl.ProjectViewImpl
|
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
import com.intellij.openapi.project.DumbAwareAction
|
import com.intellij.openapi.project.DumbAwareAction
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.ui.getUserData
|
||||||
import com.intellij.openapi.project.ProjectManager
|
import com.intellij.openapi.ui.putUserData
|
||||||
import com.intellij.openapi.startup.ProjectActivity
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.wm.ToolWindow
|
|
||||||
import com.intellij.openapi.wm.ToolWindowId
|
import com.intellij.openapi.wm.ToolWindowId
|
||||||
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
|
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
|
||||||
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
|
|
||||||
import com.intellij.ui.KeyStrokeAdapter
|
import com.intellij.ui.KeyStrokeAdapter
|
||||||
import com.intellij.ui.TreeExpandCollapse
|
import com.intellij.ui.TreeExpandCollapse
|
||||||
import com.intellij.ui.speedSearch.SpeedSearchSupply
|
import com.intellij.ui.speedSearch.SpeedSearchSupply
|
||||||
@ -53,6 +49,8 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
|
import javax.swing.JComponent
|
||||||
|
import javax.swing.JTree
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
@ -132,7 +130,6 @@ internal class NerdTree : VimExtension {
|
|||||||
|
|
||||||
synchronized(Util.monitor) {
|
synchronized(Util.monitor) {
|
||||||
Util.commandsRegistered = true
|
Util.commandsRegistered = true
|
||||||
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,39 +161,8 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProjectViewListener(private val project: Project) : ToolWindowManagerListener {
|
|
||||||
override fun toolWindowShown(toolWindow: ToolWindow) {
|
|
||||||
if (ToolWindowId.PROJECT_VIEW != toolWindow.id) return
|
|
||||||
|
|
||||||
val dispatcher = NerdDispatcher.getInstance(project)
|
|
||||||
if (dispatcher.speedSearchListenerInstalled) return
|
|
||||||
|
|
||||||
// I specify nullability explicitly as we've got a lot of exceptions saying this property is null
|
|
||||||
val currentProjectViewPane: AbstractProjectViewPane? = ProjectView.getInstance(project).currentProjectViewPane
|
|
||||||
val tree = currentProjectViewPane?.tree ?: return
|
|
||||||
val supply = SpeedSearchSupply.getSupply(tree, true) ?: return
|
|
||||||
|
|
||||||
// NB: Here might be some issues with concurrency, but it's not really bad, I think
|
|
||||||
dispatcher.speedSearchListenerInstalled = true
|
|
||||||
supply.addChangeListener {
|
|
||||||
dispatcher.waitForSearch = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
installDispatcher(project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NerdDispatcher : DumbAwareAction() {
|
class NerdDispatcher : DumbAwareAction() {
|
||||||
internal var waitForSearch = false
|
internal var waitForSearch = false
|
||||||
internal var speedSearchListenerInstalled = false
|
|
||||||
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
var keyStroke = getKeyStroke(e) ?: return
|
var keyStroke = getKeyStroke(e) ?: return
|
||||||
@ -244,10 +210,6 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getInstance(project: Project): NerdDispatcher {
|
|
||||||
return project.getService(NerdDispatcher::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val ESCAPE_KEY_CODE = 27
|
private const val ESCAPE_KEY_CODE = 27
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,19 +245,14 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapActivateNode",
|
"NERDTreeMapActivateNode",
|
||||||
"o",
|
"o",
|
||||||
NerdAction.Code { project, dataContext, _ ->
|
NerdAction.Code { _, dataContext, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
|
|
||||||
val array = CommonDataKeys.NAVIGATABLE_ARRAY.getData(dataContext)?.filter { it.canNavigateToSource() }
|
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
||||||
if (array.isNullOrEmpty()) {
|
if (tree.isExpanded(row)) {
|
||||||
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
tree.collapseRow(row)
|
||||||
if (tree.isExpanded(row)) {
|
|
||||||
tree.collapseRow(row)
|
|
||||||
} else {
|
|
||||||
tree.expandRow(row)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
array.forEach { it.navigate(true) }
|
tree.expandRow(row)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -374,8 +331,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapOpenRecursively",
|
"NERDTreeMapOpenRecursively",
|
||||||
"O",
|
"O",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
TreeExpandCollapse.expandAll(tree)
|
TreeExpandCollapse.expandAll(tree)
|
||||||
tree.selectionPath?.let {
|
tree.selectionPath?.let {
|
||||||
TreeUtil.scrollToVisible(tree, it, false)
|
TreeUtil.scrollToVisible(tree, it, false)
|
||||||
@ -385,8 +342,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapCloseChildren",
|
"NERDTreeMapCloseChildren",
|
||||||
"X",
|
"X",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
TreeExpandCollapse.collapse(tree)
|
TreeExpandCollapse.collapse(tree)
|
||||||
tree.selectionPath?.let {
|
tree.selectionPath?.let {
|
||||||
TreeUtil.scrollToVisible(tree, it, false)
|
TreeUtil.scrollToVisible(tree, it, false)
|
||||||
@ -396,8 +353,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapCloseDir",
|
"NERDTreeMapCloseDir",
|
||||||
"x",
|
"x",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
if (tree.isExpanded(currentPath)) {
|
if (tree.isExpanded(currentPath)) {
|
||||||
tree.collapsePath(currentPath)
|
tree.collapsePath(currentPath)
|
||||||
@ -415,8 +372,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpParent",
|
"NERDTreeMapJumpParent",
|
||||||
"p",
|
"p",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
val parentPath = currentPath.parentPath ?: return@Code
|
val parentPath = currentPath.parentPath ?: return@Code
|
||||||
if (parentPath.parentPath != null) {
|
if (parentPath.parentPath != null) {
|
||||||
@ -429,8 +386,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpFirstChild",
|
"NERDTreeMapJumpFirstChild",
|
||||||
"K",
|
"K",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
val parent = currentPath.parentPath ?: return@Code
|
val parent = currentPath.parentPath ?: return@Code
|
||||||
val row = tree.getRowForPath(parent)
|
val row = tree.getRowForPath(parent)
|
||||||
@ -442,8 +399,8 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand(
|
registerCommand(
|
||||||
"NERDTreeMapJumpLastChild",
|
"NERDTreeMapJumpLastChild",
|
||||||
"J",
|
"J",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
val tree = getTree(e) ?: return@Code
|
||||||
val currentPath = tree.selectionPath ?: return@Code
|
val currentPath = tree.selectionPath ?: return@Code
|
||||||
|
|
||||||
val currentPathCount = currentPath.pathCount
|
val currentPathCount = currentPath.pathCount
|
||||||
@ -488,18 +445,17 @@ internal class NerdTree : VimExtension {
|
|||||||
|
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"/",
|
"/",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
NerdDispatcher.getInstance(project).waitForSearch = true
|
val tree = getTree(e) ?: return@Code
|
||||||
|
tree.getUserData(KEY)?.waitForSearch = true
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
registerCommand(
|
registerCommand(
|
||||||
"<ESC>",
|
"<ESC>",
|
||||||
NerdAction.Code { project, _, _ ->
|
NerdAction.Code { _, _, e ->
|
||||||
val instance = NerdDispatcher.getInstance(project)
|
val tree = getTree(e) ?: return@Code
|
||||||
if (instance.waitForSearch) {
|
tree.getUserData(KEY)?.waitForSearch = false
|
||||||
instance.waitForSearch = false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -533,6 +489,21 @@ internal class NerdTree : VimExtension {
|
|||||||
companion object {
|
companion object {
|
||||||
const val pluginName = "NERDTree"
|
const val pluginName = "NERDTree"
|
||||||
private val LOG = logger<NerdTree>()
|
private val LOG = logger<NerdTree>()
|
||||||
|
private val KEY = Key.create<NerdDispatcher>("IdeaVim-NerdTree-Dispatcher")
|
||||||
|
|
||||||
|
fun installDispatcher(component: JComponent) {
|
||||||
|
if (component.getUserData(KEY) != null) return
|
||||||
|
|
||||||
|
val dispatcher = NerdDispatcher()
|
||||||
|
component.putUserData(KEY, dispatcher)
|
||||||
|
|
||||||
|
val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
|
||||||
|
dispatcher.registerCustomShortcutSet(KeyGroup.toShortcutSet(shortcuts), component)
|
||||||
|
|
||||||
|
SpeedSearchSupply.getSupply(component, true)?.addChangeListener {
|
||||||
|
dispatcher.waitForSearch = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,12 +538,6 @@ private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installDispatcher(project: Project) {
|
private fun getTree(e: AnActionEvent): JTree? {
|
||||||
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
|
||||||
val shortcuts =
|
|
||||||
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
|
||||||
dispatcher.registerCustomShortcutSet(
|
|
||||||
KeyGroup.toShortcutSet(shortcuts),
|
|
||||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.maddyhome.idea.vim.extension.nerdtree
|
||||||
|
|
||||||
|
import com.intellij.ide.ApplicationInitializedListener
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.util.ui.StartupUiUtil
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import java.awt.AWTEvent
|
||||||
|
import java.awt.event.FocusEvent
|
||||||
|
import javax.swing.JTree
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
internal class NerdTreeApplicationListener : ApplicationInitializedListener {
|
||||||
|
override suspend fun execute(asyncScope: CoroutineScope) {
|
||||||
|
StartupUiUtil.addAwtListener(::handleEvent, AWTEvent.FOCUS_EVENT_MASK, ApplicationManager.getApplication().getService(NerdTreeDisposableService::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEvent(event: AWTEvent) {
|
||||||
|
if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED) {
|
||||||
|
val source = event.source
|
||||||
|
if (source is JTree) {
|
||||||
|
NerdTree.installDispatcher(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.maddyhome.idea.vim.extension.nerdtree
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.components.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
internal class NerdTreeDisposableService : Disposable {
|
||||||
|
override fun dispose() {}
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.extension.replacewithregister
|
package com.maddyhome.idea.vim.extension.replacewithregister
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
@ -64,7 +65,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
val selectionEnd = caret.selectionEnd
|
val selectionEnd = caret.selectionEnd
|
||||||
|
|
||||||
val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
||||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||||
}
|
}
|
||||||
editor.exitVisualMode()
|
editor.exitVisualMode()
|
||||||
}
|
}
|
||||||
@ -92,7 +93,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
||||||
caretsAndSelections += visualSelection
|
caretsAndSelections += visualSelection
|
||||||
|
|
||||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.sortedCarets().forEach { caret ->
|
editor.sortedCarets().forEach { caret ->
|
||||||
@ -120,7 +121,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||||
)
|
)
|
||||||
// todo multicaret
|
// todo multicaret
|
||||||
doReplace(ijEditor, editor.primaryCaret(), visualSelection)
|
doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +141,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||||
val registerGroup = injector.registerGroup
|
val registerGroup = injector.registerGroup
|
||||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||||
@ -168,7 +169,7 @@ private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection:
|
|||||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||||
VimPlugin.getPut().putText(
|
VimPlugin.getPut().putText(
|
||||||
vimEditor,
|
vimEditor,
|
||||||
injector.executionContextManager.onEditor(editor.vim),
|
context.vim,
|
||||||
putData,
|
putData,
|
||||||
operatorArguments = OperatorArguments(
|
operatorArguments = OperatorArguments(
|
||||||
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||||
|
@ -118,7 +118,7 @@ internal class IdeaVimSneakExtension : VimExtension {
|
|||||||
var lastSymbols: String = ""
|
var lastSymbols: String = ""
|
||||||
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
||||||
val caret = editor.primaryCaret()
|
val caret = editor.primaryCaret()
|
||||||
val position = caret.offset.point
|
val position = caret.offset
|
||||||
val chars = editor.text()
|
val chars = editor.text()
|
||||||
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
||||||
if (foundPosition != null) {
|
if (foundPosition != null) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.extension.surround
|
package com.maddyhome.idea.vim.extension.surround
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.application.runWriteAction
|
import com.intellij.openapi.application.runWriteAction
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
@ -34,6 +35,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
|
|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
@ -44,7 +46,6 @@ import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
|||||||
import com.maddyhome.idea.vim.put.PutData
|
import com.maddyhome.idea.vim.put.PutData
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.state.mode.mode
|
|
||||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
@ -100,7 +101,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val ijEditor = editor.ij
|
val ijEditor = editor.ij
|
||||||
val c = getChar(ijEditor)
|
val c = getChar(ijEditor)
|
||||||
if (c.code == 0) return
|
if (c.code == 0) return
|
||||||
val pair = getOrInputPair(c, ijEditor) ?: return
|
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return
|
||||||
|
|
||||||
editor.forEachCaret {
|
editor.forEachCaret {
|
||||||
val line = it.getBufferPosition().line
|
val line = it.getBufferPosition().line
|
||||||
@ -150,7 +151,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val charTo = getChar(editor.ij)
|
val charTo = getChar(editor.ij)
|
||||||
if (charTo.code == 0) return
|
if (charTo.code == 0) return
|
||||||
|
|
||||||
val newSurround = getOrInputPair(charTo, editor.ij) ?: return
|
val newSurround = getOrInputPair(charTo, editor.ij, context.ij) ?: return
|
||||||
runWriteAction { change(editor, context, charFrom, newSurround) }
|
runWriteAction { change(editor, context, charFrom, newSurround) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,12 +228,12 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val searchHelper = injector.searchHelper
|
val searchHelper = injector.searchHelper
|
||||||
return when (char) {
|
return when (char) {
|
||||||
't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
|
't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
|
||||||
'(', ')', 'b' -> searchHelper.findBlockRange(editor, caret, '(', 1, true)
|
'(', ')', 'b' -> findBlockRange(editor, caret, '(', 1, true)
|
||||||
'[', ']' -> searchHelper.findBlockRange(editor, caret, '[', 1, true)
|
'[', ']' -> findBlockRange(editor, caret, '[', 1, true)
|
||||||
'{', '}', 'B' -> searchHelper.findBlockRange(editor, caret, '{', 1, true)
|
'{', '}', 'B' -> findBlockRange(editor, caret, '{', 1, true)
|
||||||
'<', '>' -> searchHelper.findBlockRange(editor, caret, '<', 1, true)
|
'<', '>' -> findBlockRange(editor, caret, '<', 1, true)
|
||||||
'`', '\'', '"' -> {
|
'`', '\'', '"' -> {
|
||||||
val caretOffset = caret.offset.point
|
val caretOffset = caret.offset
|
||||||
val text = editor.text()
|
val text = editor.text()
|
||||||
if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
|
if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
|
||||||
TextRange(caretOffset - 1, caretOffset + 1)
|
TextRange(caretOffset - 1, caretOffset + 1)
|
||||||
@ -269,23 +270,23 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
||||||
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||||
val editor = vimEditor.ij
|
val ijEditor = vimEditor.ij
|
||||||
val c = getChar(editor)
|
val c = getChar(ijEditor)
|
||||||
if (c.code == 0) return true
|
if (c.code == 0) return true
|
||||||
|
|
||||||
val pair = getOrInputPair(c, editor) ?: return false
|
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
||||||
|
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val change = VimPlugin.getChange()
|
val change = VimPlugin.getChange()
|
||||||
if (supportsMultipleCursors) {
|
if (supportsMultipleCursors) {
|
||||||
editor.runWithEveryCaretAndRestore {
|
ijEditor.runWithEveryCaretAndRestore {
|
||||||
applyOnce(editor, change, pair, count)
|
applyOnce(ijEditor, change, pair, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
applyOnce(editor, change, pair, count)
|
applyOnce(ijEditor, change, pair, count)
|
||||||
// Jump back to start
|
// Jump back to start
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
|
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -347,8 +348,8 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
||||||
val tagInput = inputString(editor, "<", '>')
|
val tagInput = inputString(editor, context, "<", '>')
|
||||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||||
return if (matcher.find()) {
|
return if (matcher.find()) {
|
||||||
val tagName = matcher.group(1)
|
val tagName = matcher.group(1)
|
||||||
@ -361,17 +362,18 @@ private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
|||||||
|
|
||||||
private fun inputFunctionName(
|
private fun inputFunctionName(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
|
context: DataContext,
|
||||||
withInternalSpaces: Boolean,
|
withInternalSpaces: Boolean,
|
||||||
): Pair<String, String>? {
|
): Pair<String, String>? {
|
||||||
val functionNameInput = inputString(editor, "function: ", null)
|
val functionNameInput = inputString(editor, context, "function: ", null)
|
||||||
if (functionNameInput.isEmpty()) return null
|
if (functionNameInput.isEmpty()) return null
|
||||||
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = when (c) {
|
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
|
||||||
'<', 't' -> inputTagPair(editor)
|
'<', 't' -> inputTagPair(editor, context)
|
||||||
'f' -> inputFunctionName(editor, false)
|
'f' -> inputFunctionName(editor, context, false)
|
||||||
'F' -> inputFunctionName(editor, true)
|
'F' -> inputFunctionName(editor, context, true)
|
||||||
else -> getSurroundPair(c)
|
else -> getSurroundPair(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val allowWrap = injector.options(editor).whichwrap.contains("~")
|
val allowWrap = injector.options(editor).whichwrap.contains("~")
|
||||||
var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
|
var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
|
||||||
if (motion is Motion.Error) return false
|
if (motion is Motion.Error) return false
|
||||||
changeCase(editor, caret, caret.offset.point, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||||
motion = injector.motion.getHorizontalMotion(
|
motion = injector.motion.getHorizontalMotion(
|
||||||
editor,
|
editor,
|
||||||
caret,
|
caret,
|
||||||
@ -235,8 +235,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
val lineLength = editor.lineLength(line)
|
val lineLength = editor.lineLength(line)
|
||||||
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
|
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
|
||||||
val pad =
|
val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
|
||||||
EditorHelper.pad((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context, line, column)
|
|
||||||
val offset = editor.getLineEndOffset(line)
|
val offset = editor.getLineEndOffset(line)
|
||||||
insertText(editor, caret, offset, pad)
|
insertText(editor, caret, offset, pad)
|
||||||
}
|
}
|
||||||
@ -395,7 +394,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
) {
|
) {
|
||||||
val startPos = editor.offsetToBufferPosition(caret.offset.point)
|
val startPos = editor.offsetToBufferPosition(caret.offset)
|
||||||
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
||||||
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
@ -451,7 +450,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
dir: Int,
|
dir: Int,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
) {
|
) {
|
||||||
val start = caret.offset.point
|
val start = caret.offset
|
||||||
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
|
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
|
||||||
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
|
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
|
||||||
}
|
}
|
||||||
@ -485,7 +484,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
|||||||
|
|
||||||
// Remember the current caret column
|
// Remember the current caret column
|
||||||
val intendedColumn = caret.vimLastColumn
|
val intendedColumn = caret.vimLastColumn
|
||||||
val indentConfig = create((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context)
|
val indentConfig = create((editor as IjVimEditor).editor)
|
||||||
val sline = editor.offsetToBufferPosition(range.startOffset).line
|
val sline = editor.offsetToBufferPosition(range.startOffset).line
|
||||||
val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
|
val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
|
||||||
val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
|
val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
|
||||||
|
@ -235,7 +235,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
|
// 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.
|
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
|
||||||
Runnable switchToInsertMode = () -> {
|
Runnable switchToInsertMode = () -> {
|
||||||
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
|
||||||
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
|
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
|
||||||
KeyHandler.getInstance().reset(new IjVimEditor(editor));
|
KeyHandler.getInstance().reset(new IjVimEditor(editor));
|
||||||
};
|
};
|
||||||
|
@ -22,9 +22,12 @@ import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
|||||||
import com.intellij.openapi.fileTypes.FileType;
|
import com.intellij.openapi.fileTypes.FileType;
|
||||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.project.ProjectManager;
|
||||||
import com.intellij.openapi.roots.ProjectRootManager;
|
import com.intellij.openapi.roots.ProjectRootManager;
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
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.FilenameIndex;
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import com.intellij.psi.search.GlobalSearchScope;
|
||||||
import com.intellij.psi.search.ProjectScope;
|
import com.intellij.psi.search.ProjectScope;
|
||||||
@ -44,6 +47,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||||
@ -445,4 +449,28 @@ public class FileGroup extends VimFileBase {
|
|||||||
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
}
|
@ -11,41 +11,24 @@ import com.intellij.openapi.actionSystem.DataContext
|
|||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.editor.Caret
|
import com.intellij.openapi.editor.Caret
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.LogicalPosition
|
|
||||||
import com.intellij.openapi.editor.VisualPosition
|
import com.intellij.openapi.editor.VisualPosition
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||||
import com.intellij.openapi.fileEditor.TextEditor
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
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.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.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
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.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.getLeadingCharacterOffset
|
||||||
import com.maddyhome.idea.vim.api.getVisualLineCount
|
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.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.normalizeVisualColumn
|
||||||
import com.maddyhome.idea.vim.api.normalizeVisualLine
|
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.api.visualLineToBufferLine
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.MotionType
|
import com.maddyhome.idea.vim.command.MotionType
|
||||||
@ -54,12 +37,9 @@ import com.maddyhome.idea.vim.common.TextRange
|
|||||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||||
import com.maddyhome.idea.vim.handler.Motion
|
import com.maddyhome.idea.vim.handler.Motion
|
||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
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.MotionActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
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.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.fileSize
|
import com.maddyhome.idea.vim.helper.fileSize
|
||||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||||
@ -67,17 +47,13 @@ import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
|||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
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.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.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||||
import org.jetbrains.annotations.Range
|
import org.jetbrains.annotations.Range
|
||||||
import java.io.File
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@ -90,24 +66,6 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
|
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(
|
override fun moveCaretToFirstDisplayLine(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
caret: ImmutableVimCaret,
|
caret: ImmutableVimCaret,
|
||||||
@ -130,85 +88,12 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
return moveCaretToScreenLocation(editor.ij, caret.ij, ScreenLocation.MIDDLE, 0, false)
|
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 {
|
override fun moveCaretToCurrentDisplayLineMiddle(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||||
val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
|
val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
|
||||||
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
||||||
return moveCaretToColumn(editor, caret, max(0, min(len - 1, width)), false)
|
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 {
|
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||||
return moveCaretToColumn(editor, caret, col, false)
|
return moveCaretToColumn(editor, caret, col, false)
|
||||||
@ -219,7 +104,7 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
caret: ImmutableVimCaret,
|
caret: ImmutableVimCaret,
|
||||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||||
val bufferLine = caret.getLine().line
|
val bufferLine = caret.getLine()
|
||||||
return editor.getLeadingCharacterOffset(bufferLine, col)
|
return editor.getLeadingCharacterOffset(bufferLine, col)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,36 +117,6 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
return moveCaretToColumn(editor, caret, col, allowEnd)
|
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.
|
* If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
|
||||||
*/
|
*/
|
||||||
@ -279,30 +134,18 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||||
switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
|
switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
|
||||||
return editor.currentCaret().offset.point
|
return editor.currentCaret().offset
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||||
val absolute = rawCount >= 1
|
val absolute = rawCount >= 1
|
||||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||||
switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
|
switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
|
||||||
return editor.currentCaret().offset.point
|
return editor.currentCaret().offset
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
private enum class ScreenLocation {
|
||||||
|
@ -538,20 +538,24 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
*
|
*
|
||||||
* @param editor The editor to search in
|
* @param editor The editor to search in
|
||||||
* @param caret The caret to use for initial search offset, and to move for interactive substitution
|
* @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 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 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]`
|
* @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
|
@Override
|
||||||
@RWLockLabel.SelfSynchronized
|
@RWLockLabel.SelfSynchronized
|
||||||
public boolean processSubstituteCommand(@NotNull VimEditor editor,
|
public boolean processSubstituteCommand(@NotNull VimEditor editor,
|
||||||
@NotNull VimCaret caret,
|
@NotNull VimCaret caret,
|
||||||
|
@NotNull ExecutionContext context,
|
||||||
@NotNull LineRange range,
|
@NotNull LineRange range,
|
||||||
@NotNull @NonNls String excmd,
|
@NotNull @NonNls String excmd,
|
||||||
@NotNull @NonNls String exarg,
|
@NotNull @NonNls String exarg,
|
||||||
@NotNull VimLContext parent) {
|
@NotNull VimLContext parent) {
|
||||||
if (globalIjOptions(injector).getUseNewRegex()) return super.processSubstituteCommand(editor, caret, range, excmd, exarg, parent);
|
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||||
|
return super.processSubstituteCommand(editor, caret, context, 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.
|
// 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<>();
|
List<ExException> exceptions = new ArrayList<>();
|
||||||
@ -808,7 +812,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
RangeHighlighter hl =
|
RangeHighlighter hl =
|
||||||
SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
|
SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
|
||||||
endoff);
|
endoff);
|
||||||
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), match, ((IjVimCaret)caret).getCaret(), startoff);
|
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), context, match, ((IjVimCaret)caret).getCaret(), startoff);
|
||||||
((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
|
((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case SUBSTITUTE_THIS:
|
case SUBSTITUTE_THIS:
|
||||||
@ -837,8 +841,7 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
caret.moveToOffset(startoff);
|
caret.moveToOffset(startoff);
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
try {
|
try {
|
||||||
match =
|
match = expression.evaluate(editor, context, parent).toInsertableString();
|
||||||
expression.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent).toInsertableString();
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
exceptions.add((ExException)e);
|
exceptions.add((ExException)e);
|
||||||
@ -989,7 +992,9 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
|
return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
|
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor,
|
||||||
|
@NotNull ExecutionContext context,
|
||||||
|
@NotNull String match, @NotNull Caret caret, int startoff) {
|
||||||
final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
|
final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
|
||||||
final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
|
final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
|
||||||
final ReplaceConfirmationChoice choice;
|
final ReplaceConfirmationChoice choice;
|
||||||
@ -1023,7 +1028,6 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
else {
|
else {
|
||||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
||||||
final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
|
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);
|
exEntryPanel.activate(editor, ((IjEditorExecutionContext)context).getContext(), MessageHelper.message("replace.with.0", match), "", 1);
|
||||||
new IjVimCaret(caret).moveToOffset(startoff);
|
new IjVimCaret(caret).moveToOffset(startoff);
|
||||||
ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
|
ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
|
||||||
@ -1081,9 +1085,9 @@ public class SearchGroup extends IjVimSearchGroup implements PersistentStateComp
|
|||||||
private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
|
private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
|
||||||
if (forwards) {
|
if (forwards) {
|
||||||
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
|
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
|
||||||
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset().getPoint(), count, searchOptions);
|
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset(), count, searchOptions);
|
||||||
} else {
|
} else {
|
||||||
return searchBackward(editor, editor.primaryCaret().getOffset().getPoint(), count);
|
return searchBackward(editor, editor.primaryCaret().getOffset(), count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ internal object IdeaSelectionControl {
|
|||||||
is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
|
is Mode.VISUAL -> VimPlugin.getVisualMotion().enterVisualMode(editor.vim, mode.selectionType)
|
||||||
is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
|
is Mode.SELECT -> VimPlugin.getVisualMotion().enterSelectMode(editor.vim, mode.selectionType)
|
||||||
is Mode.INSERT -> VimPlugin.getChange()
|
is Mode.INSERT -> VimPlugin.getChange()
|
||||||
.insertBeforeCursor(editor.vim, injector.executionContextManager.onEditor(editor.vim))
|
.insertBeforeCursor(editor.vim, injector.executionContextManager.getEditorExecutionContext(editor.vim))
|
||||||
|
|
||||||
is Mode.NORMAL -> Unit
|
is Mode.NORMAL -> Unit
|
||||||
else -> error("Unexpected mode: $mode")
|
else -> error("Unexpected mode: $mode")
|
||||||
|
@ -337,7 +337,7 @@ internal abstract class VimKeyHandler(nextHandler: EditorActionHandler?) : Octop
|
|||||||
|
|
||||||
override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
override fun executeHandler(editor: Editor, caret: Caret?, dataContext: DataContext?) {
|
||||||
val enterKey = key(key)
|
val enterKey = key(key)
|
||||||
val context = injector.executionContextManager.onEditor(editor.vim, dataContext?.vim)
|
val context = dataContext?.vim ?: injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
|
keyHandler.handleKey(editor.vim, enterKey, context, keyHandler.keyHandlerState)
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,8 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
|
|||||||
|
|
||||||
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
||||||
// NOTE: At the moment, this causes project leak in tests
|
// 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) {
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
(this as? EditorEx)?.setCaretVisible(true)
|
(this as? EditorEx)?.setCaretVisible(true)
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ package com.maddyhome.idea.vim.helper
|
|||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.maddyhome.idea.vim.action.change.Extension
|
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.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||||
@ -23,7 +23,7 @@ import javax.swing.KeyStroke
|
|||||||
@Service
|
@Service
|
||||||
internal class CommandLineHelper : VimCommandLineHelper {
|
internal class CommandLineHelper : VimCommandLineHelper {
|
||||||
|
|
||||||
override fun inputString(vimEditor: VimEditor, prompt: String, finishOn: Char?): String? {
|
override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
|
||||||
val editor = vimEditor.ij
|
val editor = vimEditor.ij
|
||||||
if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
|
if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
|
||||||
val input = Extension.consumeString()
|
val input = Extension.consumeString()
|
||||||
@ -53,7 +53,7 @@ internal class CommandLineHelper : VimCommandLineHelper {
|
|||||||
var text: String? = null
|
var text: String? = null
|
||||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
|
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for input()
|
||||||
val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
|
val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
|
||||||
exEntryPanel.activate(editor, injector.executionContextManager.onEditor(editor.vim).ij, prompt.ifEmpty { " " }, "", 1)
|
exEntryPanel.activate(editor, context.ij, prompt.ifEmpty { " " }, "", 1)
|
||||||
ModalEntry.activate(editor.vim) { key: KeyStroke ->
|
ModalEntry.activate(editor.vim) { key: KeyStroke ->
|
||||||
return@activate when {
|
return@activate when {
|
||||||
key.isCloseKeyStroke() -> {
|
key.isCloseKeyStroke() -> {
|
||||||
|
@ -14,6 +14,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil
|
|||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.UserDataHolder
|
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(
|
internal class EditorDataContext @Deprecated("Please use `init` method") constructor(
|
||||||
private val editor: Editor,
|
private val editor: Editor,
|
||||||
private val editorContext: DataContext,
|
private val editorContext: DataContext,
|
||||||
|
@ -211,15 +211,12 @@ public class EditorHelper {
|
|||||||
return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
|
return injector.getEditorGroup().getEditors(new IjVimDocument(doc)).stream().findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull String pad(final @NotNull Editor editor,
|
public static @NotNull String pad(final @NotNull Editor editor, int line, final int to) {
|
||||||
@NotNull DataContext context,
|
|
||||||
int line,
|
|
||||||
final int to) {
|
|
||||||
final int len = EngineEditorHelperKt.lineLength(new IjVimEditor(editor), line);
|
final int len = EngineEditorHelperKt.lineLength(new IjVimEditor(editor), line);
|
||||||
if (len >= to) return "";
|
if (len >= to) return "";
|
||||||
|
|
||||||
final int limit = to - len;
|
final int limit = to - len;
|
||||||
return IndentConfig.create(editor, context).createIndentBySize(limit);
|
return IndentConfig.create(editor).createIndentBySize(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +35,6 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
|||||||
import com.maddyhome.idea.vim.api.NativeAction
|
import com.maddyhome.idea.vim.api.NativeAction
|
||||||
import com.maddyhome.idea.vim.api.VimActionExecutor
|
import com.maddyhome.idea.vim.api.VimActionExecutor
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
||||||
import com.maddyhome.idea.vim.newapi.IjNativeAction
|
import com.maddyhome.idea.vim.newapi.IjNativeAction
|
||||||
@ -79,6 +78,7 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
val dataContext = DataContextWrapper(context.ij)
|
val dataContext = DataContextWrapper(context.ij)
|
||||||
dataContext.putUserData(runFromVimKey, true)
|
dataContext.putUserData(runFromVimKey, true)
|
||||||
|
|
||||||
|
val actionId = ActionManager.getInstance().getId(ijAction)
|
||||||
val event = AnActionEvent(
|
val event = AnActionEvent(
|
||||||
null,
|
null,
|
||||||
dataContext,
|
dataContext,
|
||||||
@ -92,8 +92,15 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
// because rider use async update method. See VIM-1819.
|
// because rider use async update method. See VIM-1819.
|
||||||
// This method executes inside of lastUpdateAndCheckDumb
|
// This method executes inside of lastUpdateAndCheckDumb
|
||||||
// Another related issue: VIM-2604
|
// Another related issue: VIM-2604
|
||||||
ijAction.beforeActionPerformedUpdate(event)
|
|
||||||
if (!event.presentation.isEnabled) return false
|
// 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 (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
|
if (ijAction is ActionGroup && !event.presentation.isPerformGroup) {
|
||||||
// Some ActionGroups should not be performed, but shown as a popup
|
// Some ActionGroups should not be performed, but shown as a popup
|
||||||
val popup = JBPopupFactory.getInstance()
|
val popup = JBPopupFactory.getInstance()
|
||||||
@ -217,7 +224,7 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
CommandProcessor.getInstance()
|
CommandProcessor.getInstance()
|
||||||
.executeCommand(
|
.executeCommand(
|
||||||
editor.ij.project,
|
editor.ij.project,
|
||||||
{ cmd.execute(editor, injector.executionContextManager.onEditor(editor, context), operatorArguments) },
|
{ cmd.execute(editor, context, operatorArguments) },
|
||||||
cmd.id,
|
cmd.id,
|
||||||
DocCommandGroupId.noneGroupId(editor.ij.document),
|
DocCommandGroupId.noneGroupId(editor.ij.document),
|
||||||
UndoConfirmationPolicy.DEFAULT,
|
UndoConfirmationPolicy.DEFAULT,
|
||||||
|
@ -14,7 +14,6 @@ import com.intellij.openapi.editor.VisualPosition
|
|||||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
|
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.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
@ -51,8 +50,8 @@ internal class IjEditorHelper : EngineEditorHelper {
|
|||||||
return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
|
return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pad(editor: VimEditor, context: ExecutionContext, line: Int, to: Int): String {
|
override fun pad(editor: VimEditor, line: Int, to: Int): String {
|
||||||
return EditorHelper.pad(editor.ij, context.ij, line, to)
|
return EditorHelper.pad(editor.ij, line, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
|
override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
|
||||||
|
@ -632,113 +632,6 @@ public class SearchHelper {
|
|||||||
return new TextRange(bstart, bend + 1);
|
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,
|
private static int findBlockLocation(@NotNull CharSequence chars,
|
||||||
char found,
|
char found,
|
||||||
char match,
|
char match,
|
||||||
|
@ -10,10 +10,13 @@ package com.maddyhome.idea.vim.helper
|
|||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
|
import com.intellij.openapi.application.ApplicationInfo
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
import com.intellij.openapi.command.undo.UndoManager
|
import com.intellij.openapi.command.undo.UndoManager
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
|
import com.intellij.openapi.fileEditor.TextEditor
|
||||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
|
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
|
||||||
|
import com.intellij.openapi.util.registry.Registry
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@ -40,23 +43,11 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
val scrollingModel = editor.getScrollingModel()
|
val scrollingModel = editor.getScrollingModel()
|
||||||
scrollingModel.accumulateViewportChanges()
|
scrollingModel.accumulateViewportChanges()
|
||||||
|
|
||||||
if (injector.globalIjOptions().oldundo) {
|
// [VERSION UPDATE] 241+ remove this if
|
||||||
SelectionVimListenerSuppressor.lock().use { undoManager.undo(fileEditor) }
|
if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
|
||||||
restoreVisualMode(editor)
|
undoFor241plus(editor, undoManager, fileEditor)
|
||||||
} else {
|
} else {
|
||||||
// TODO refactor me after VIM-308 when restoring selection and caret movement will be ignored by undo
|
undoForLessThan241(undoManager, fileEditor, editor)
|
||||||
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()
|
scrollingModel.flushViewportChanges()
|
||||||
@ -66,6 +57,61 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
return false
|
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)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// We execute undo one more time if the previous one just restored selection
|
||||||
|
if (!hasChanges && hasSelection(editor) && undoManager.isUndoAvailable(fileEditor)) {
|
||||||
|
undoManager.undo(fileEditor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||||
|
removeSelections(editor)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runWithBooleanRegistryOption("ide.undo.transparent.caret.movement", true) {
|
||||||
|
undoManager.undo(fileEditor)
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandProcessor.getInstance().runUndoTransparentAction {
|
||||||
|
removeSelections(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun hasSelection(editor: VimEditor): Boolean {
|
private fun hasSelection(editor: VimEditor): Boolean {
|
||||||
return editor.primaryCaret().ij.hasSelection()
|
return editor.primaryCaret().ij.hasSelection()
|
||||||
}
|
}
|
||||||
@ -76,7 +122,23 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor.ij)
|
val fileEditor = TextEditorProvider.getInstance().getTextEditor(editor.ij)
|
||||||
val undoManager = UndoManager.getInstance(project)
|
val undoManager = UndoManager.getInstance(project)
|
||||||
if (undoManager.isRedoAvailable(fileEditor)) {
|
if (undoManager.isRedoAvailable(fileEditor)) {
|
||||||
if (injector.globalIjOptions().oldundo) {
|
// [VERSION UPDATE] 241+ remove this if
|
||||||
|
if (ApplicationInfo.getInstance().build.baselineVersion >= 241) {
|
||||||
|
redoFor241Plus(undoManager, fileEditor, editor)
|
||||||
|
} else {
|
||||||
|
redoForLessThan241(undoManager, fileEditor, editor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun redoForLessThan241(
|
||||||
|
undoManager: UndoManager,
|
||||||
|
fileEditor: TextEditor,
|
||||||
|
editor: VimEditor,
|
||||||
|
) {if (injector.globalIjOptions().oldundo) {
|
||||||
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
SelectionVimListenerSuppressor.lock().use { undoManager.redo(fileEditor) }
|
||||||
restoreVisualMode(editor)
|
restoreVisualMode(editor)
|
||||||
} else {
|
} else {
|
||||||
@ -88,19 +150,50 @@ internal class UndoRedoHelper : UndoRedoBase() {
|
|||||||
editor.runWithChangeTracking {
|
editor.runWithChangeTracking {
|
||||||
undoManager.redo(fileEditor)
|
undoManager.redo(fileEditor)
|
||||||
|
|
||||||
// We execute undo one more time if the previous one just restored selection
|
// We execute undo one more time if the previous one just restored selection
|
||||||
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
if (!hasChanges && hasSelection(editor) && undoManager.isRedoAvailable(fileEditor)) {
|
||||||
undoManager.redo(fileEditor)
|
undoManager.redo(fileEditor)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandProcessor.getInstance().runUndoTransparentAction {
|
|
||||||
removeSelections(editor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeSelections(editor: VimEditor) {
|
private fun removeSelections(editor: VimEditor) {
|
||||||
@ -114,6 +207,17 @@ 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) {
|
private fun VimEditor.runWithChangeTracking(block: ChangeTracker.() -> Unit) {
|
||||||
val tracker = ChangeTracker(this)
|
val tracker = ChangeTracker(this)
|
||||||
tracker.block()
|
tracker.block()
|
||||||
|
@ -19,6 +19,7 @@ import com.intellij.codeInsight.template.TemplateManagerListener
|
|||||||
import com.intellij.codeInsight.template.impl.TemplateState
|
import com.intellij.codeInsight.template.impl.TemplateState
|
||||||
import com.intellij.find.FindModelListener
|
import com.intellij.find.FindModelListener
|
||||||
import com.intellij.openapi.actionSystem.ActionManager
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
|
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||||
import com.intellij.openapi.actionSystem.AnAction
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.AnActionResult
|
import com.intellij.openapi.actionSystem.AnActionResult
|
||||||
@ -181,7 +182,7 @@ internal object IdeaSpecifics {
|
|||||||
if (editor.vim.inNormalMode) {
|
if (editor.vim.inNormalMode) {
|
||||||
VimPlugin.getChange().insertBeforeCursor(
|
VimPlugin.getChange().insertBeforeCursor(
|
||||||
editor.vim,
|
editor.vim,
|
||||||
injector.executionContextManager.onEditor(editor.vim),
|
injector.executionContextManager.getEditorExecutionContext(editor.vim),
|
||||||
)
|
)
|
||||||
KeyHandler.getInstance().reset(editor.vim)
|
KeyHandler.getInstance().reset(editor.vim)
|
||||||
}
|
}
|
||||||
@ -231,5 +232,7 @@ internal class FindActionIdAction : DumbAwareToggleAction() {
|
|||||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||||
injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
|
injector.globalIjOptions().trackactionids = !injector.globalIjOptions().trackactionids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
@ -35,6 +35,7 @@ import com.intellij.openapi.editor.ex.DocumentEx
|
|||||||
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
|
import com.intellij.openapi.editor.ex.EditorEventMulticasterEx
|
||||||
import com.intellij.openapi.editor.ex.FocusChangeListener
|
import com.intellij.openapi.editor.ex.FocusChangeListener
|
||||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||||
|
import com.intellij.openapi.editor.impl.EditorImpl
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener
|
||||||
@ -45,11 +46,14 @@ import com.intellij.openapi.fileEditor.ex.FileEditorWithProvider
|
|||||||
import com.intellij.openapi.fileEditor.impl.EditorComposite
|
import com.intellij.openapi.fileEditor.impl.EditorComposite
|
||||||
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
import com.intellij.openapi.fileEditor.impl.EditorWindow
|
||||||
import com.intellij.openapi.project.ProjectManager
|
import com.intellij.openapi.project.ProjectManager
|
||||||
|
import com.intellij.openapi.rd.createLifetime
|
||||||
|
import com.intellij.openapi.rd.createNestedDisposable
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.removeUserData
|
import com.intellij.openapi.util.removeUserData
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.util.ExceptionUtil
|
import com.intellij.util.ExceptionUtil
|
||||||
|
import com.jetbrains.rd.util.lifetime.Lifetime
|
||||||
import com.maddyhome.idea.vim.EventFacade
|
import com.maddyhome.idea.vim.EventFacade
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.KeyHandlerStateResetter
|
import com.maddyhome.idea.vim.KeyHandlerStateResetter
|
||||||
@ -101,7 +105,6 @@ import com.maddyhome.idea.vim.ui.widgets.macro.MacroWidgetListener
|
|||||||
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
|
import com.maddyhome.idea.vim.ui.widgets.macro.macroWidgetOptionListener
|
||||||
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
|
import com.maddyhome.idea.vim.ui.widgets.mode.listeners.ModeWidgetListener
|
||||||
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
|
import com.maddyhome.idea.vim.ui.widgets.mode.modeWidgetOptionListener
|
||||||
import com.maddyhome.idea.vim.vimDisposable
|
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
import javax.swing.SwingUtilities
|
import javax.swing.SwingUtilities
|
||||||
@ -264,12 +267,10 @@ internal object VimListenerManager {
|
|||||||
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
|
// TODO: If the user changes the 'ideavimsupport' option, existing editors won't be initialised
|
||||||
if (vimDisabled(editor)) return
|
if (vimDisabled(editor)) return
|
||||||
|
|
||||||
// As I understand, there is no need to pass a disposable that also disposes on editor close
|
val pluginLifetime = VimPlugin.getInstance().createLifetime()
|
||||||
// because all editor resources will be garbage collected anyway on editor close
|
val editorLifetime = (editor as EditorImpl).disposable.createLifetime()
|
||||||
// Note that this uses the plugin's main disposable, rather than VimPlugin.onOffDisposable, because we don't need
|
val disposable =
|
||||||
// to - we explicitly call VimListenerManager.removeAll from VimPlugin.turnOffPlugin, and this disposes each
|
Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("MyLifetimedDisposable")
|
||||||
// editor's disposable individually.
|
|
||||||
val disposable = editor.project?.vimDisposable ?: return
|
|
||||||
|
|
||||||
val listenersDisposable = Disposer.newDisposable(disposable)
|
val listenersDisposable = Disposer.newDisposable(disposable)
|
||||||
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
editor.putUserData(editorListenersDisposableKey, listenersDisposable)
|
||||||
@ -538,15 +539,15 @@ internal object VimListenerManager {
|
|||||||
// When starting on an empty line and dragging vertically upwards onto
|
// When starting on an empty line and dragging vertically upwards onto
|
||||||
// another line, the selection should include the entirety of the empty line
|
// another line, the selection should include the entirety of the empty line
|
||||||
caret.setSelection(
|
caret.setSelection(
|
||||||
ijVimEditor.coerceOffset(endOffset + 1).point,
|
ijVimEditor.coerceOffset(endOffset + 1),
|
||||||
ijVimEditor.coerceOffset(startOffset).point,
|
ijVimEditor.coerceOffset(startOffset),
|
||||||
)
|
)
|
||||||
} else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
|
} else if (lineEnd == startOffset + 1 && startOffset == endOffset) {
|
||||||
// When dragging left from EOL on a non-empty line, the selection
|
// When dragging left from EOL on a non-empty line, the selection
|
||||||
// should include the last character on the line
|
// should include the last character on the line
|
||||||
caret.setSelection(
|
caret.setSelection(
|
||||||
ijVimEditor.coerceOffset(lineEnd).point,
|
ijVimEditor.coerceOffset(lineEnd),
|
||||||
ijVimEditor.coerceOffset(lineEnd - 1).point,
|
ijVimEditor.coerceOffset(lineEnd - 1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,8 @@ import com.intellij.openapi.actionSystem.DataContext
|
|||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.UserDataHolder
|
import com.intellij.openapi.util.UserDataHolder
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
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.Editor {
|
internal open class IjEditorExecutionContext(override val context: DataContext) : ExecutionContext
|
||||||
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
|
// This key is stored in data context when the action is started from vim
|
||||||
internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
|
internal val runFromVimKey = Key.create<Boolean>("RunFromVim")
|
||||||
|
@ -9,23 +9,15 @@
|
|||||||
package com.maddyhome.idea.vim.newapi
|
package com.maddyhome.idea.vim.newapi
|
||||||
|
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContextManagerBase
|
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.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
internal class IjExecutionContextManager : ExecutionContextManagerBase() {
|
internal class IjExecutionContextManager : ExecutionContextManagerBase() {
|
||||||
override fun onEditor(editor: VimEditor, prevContext: ExecutionContext?): ExecutionContext.Editor {
|
override fun getEditorExecutionContext(editor: VimEditor): ExecutionContext {
|
||||||
if (prevContext is ExecutionContext.CaretAndEditor) {
|
return EditorUtil.getEditorDataContext(editor.ij).vim
|
||||||
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,12 +10,10 @@ package com.maddyhome.idea.vim.newapi
|
|||||||
|
|
||||||
import com.intellij.openapi.editor.RangeMarker
|
import com.intellij.openapi.editor.RangeMarker
|
||||||
import com.maddyhome.idea.vim.common.LiveRange
|
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 {
|
internal class IjLiveRange(val marker: RangeMarker) : LiveRange {
|
||||||
override val startOffset: Offset
|
override val startOffset: Int
|
||||||
get() = marker.startOffset.offset
|
get() = marker.startOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
public val RangeMarker.vim: LiveRange
|
public val RangeMarker.vim: LiveRange
|
||||||
|
@ -34,4 +34,8 @@ internal class IjNativeActionManager : NativeActionManager {
|
|||||||
public val AnAction.vim: IjNativeAction
|
public val AnAction.vim: IjNativeAction
|
||||||
get() = IjNativeAction(this)
|
get() = IjNativeAction(this)
|
||||||
|
|
||||||
public class IjNativeAction(override val action: AnAction) : NativeAction
|
public class IjNativeAction(override val action: AnAction) : NativeAction {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "IjNativeAction(action=$action)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,10 +21,7 @@ import com.maddyhome.idea.vim.api.VimCaret
|
|||||||
import com.maddyhome.idea.vim.api.VimCaretBase
|
import com.maddyhome.idea.vim.api.VimCaretBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
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.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.group.visual.VisualChange
|
||||||
import com.maddyhome.idea.vim.helper.lastSelectionInfo
|
import com.maddyhome.idea.vim.helper.lastSelectionInfo
|
||||||
import com.maddyhome.idea.vim.helper.markStorage
|
import com.maddyhome.idea.vim.helper.markStorage
|
||||||
@ -78,8 +75,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
|||||||
}
|
}
|
||||||
override val editor: VimEditor
|
override val editor: VimEditor
|
||||||
get() = IjVimEditor(caret.editor)
|
get() = IjVimEditor(caret.editor)
|
||||||
override val offset: Offset
|
override val offset: Int
|
||||||
get() = caret.offset.offset
|
get() = caret.offset
|
||||||
override var vimLastColumn: Int
|
override var vimLastColumn: Int
|
||||||
get() = caret.vimLastColumn
|
get() = caret.vimLastColumn
|
||||||
set(value) {
|
set(value) {
|
||||||
@ -118,8 +115,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
|||||||
this.caret.moveToLogicalPosition(LogicalPosition(position.line, position.column, position.leansForward))
|
this.caret.moveToLogicalPosition(LogicalPosition(position.line, position.column, position.leansForward))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLine(): EditorLine.Pointer {
|
override fun getLine(): Int {
|
||||||
return EditorLine.Pointer.init(caret.logicalPosition.line, editor)
|
return caret.logicalPosition.line
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasSelection(): Boolean {
|
override fun hasSelection(): Boolean {
|
||||||
@ -164,8 +161,8 @@ internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setSelection(start: Offset, end: Offset) {
|
override fun setSelection(start: Int, end: Int) {
|
||||||
caret.setSelection(start.point, end.point)
|
caret.setSelection(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeSelection() {
|
override fun removeSelection() {
|
||||||
|
@ -14,7 +14,6 @@ import com.intellij.openapi.editor.event.DocumentListener
|
|||||||
import com.maddyhome.idea.vim.api.VimDocument
|
import com.maddyhome.idea.vim.api.VimDocument
|
||||||
import com.maddyhome.idea.vim.common.ChangesListener
|
import com.maddyhome.idea.vim.common.ChangesListener
|
||||||
import com.maddyhome.idea.vim.common.LiveRange
|
import com.maddyhome.idea.vim.common.LiveRange
|
||||||
import com.maddyhome.idea.vim.common.Offset
|
|
||||||
|
|
||||||
internal class IjVimDocument(val document: Document) : VimDocument {
|
internal class IjVimDocument(val document: Document) : VimDocument {
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ internal class IjVimDocument(val document: Document) : VimDocument {
|
|||||||
document.removeDocumentListener(nativeListener)
|
document.removeDocumentListener(nativeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOffsetGuard(offset: Offset): LiveRange? {
|
override fun getOffsetGuard(offset: Int): LiveRange? {
|
||||||
return document.getOffsetGuard(offset.point)?.vim
|
return document.getOffsetGuard(offset)?.vim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,11 @@ import com.maddyhome.idea.vim.api.VimScrollingModel
|
|||||||
import com.maddyhome.idea.vim.api.VimSelectionModel
|
import com.maddyhome.idea.vim.api.VimSelectionModel
|
||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||||
import com.maddyhome.idea.vim.api.VirtualFile
|
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.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.common.EditorLine
|
|
||||||
import com.maddyhome.idea.vim.common.IndentConfig
|
import com.maddyhome.idea.vim.common.IndentConfig
|
||||||
import com.maddyhome.idea.vim.common.LiveRange
|
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.TextRange
|
||||||
import com.maddyhome.idea.vim.common.offset
|
|
||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
|
import com.maddyhome.idea.vim.group.visual.vimSetSystemBlockSelectionSilently
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.StrictMode
|
import com.maddyhome.idea.vim.helper.StrictMode
|
||||||
@ -90,18 +88,18 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
return editor.document.lineCount
|
return editor.document.lineCount
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteRange(leftOffset: Offset, rightOffset: Offset) {
|
override fun deleteRange(leftOffset: Int, rightOffset: Int) {
|
||||||
editor.document.deleteString(leftOffset.point, rightOffset.point)
|
editor.document.deleteString(leftOffset, rightOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addLine(atPosition: EditorLine.Offset): EditorLine.Pointer {
|
override fun addLine(atPosition: Int): Int {
|
||||||
val offset: Int = if (atPosition.line < lineCount()) {
|
val offset: Int = if (atPosition < lineCount()) {
|
||||||
// The new line character is inserted before the new line char of the previous line. So it works line an enter
|
// 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
|
// 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.
|
// \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
|
// https://youtrack.jetbrains.com/issue/IDEA-286587
|
||||||
|
|
||||||
val lineStart = (editor.document.getLineStartOffset(atPosition.line) - 1).coerceAtLeast(0)
|
val lineStart = (editor.document.getLineStartOffset(atPosition) - 1).coerceAtLeast(0)
|
||||||
val guard = editor.document.getOffsetGuard(lineStart)
|
val guard = editor.document.getOffsetGuard(lineStart)
|
||||||
if (guard != null && guard.endOffset == lineStart + 1) {
|
if (guard != null && guard.endOffset == lineStart + 1) {
|
||||||
// Dancing around guarded blocks. It may happen that this concrete position is locked, but the next
|
// Dancing around guarded blocks. It may happen that this concrete position is locked, but the next
|
||||||
@ -116,11 +114,11 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
fileSize().toInt()
|
fileSize().toInt()
|
||||||
}
|
}
|
||||||
editor.document.insertString(offset, "\n")
|
editor.document.insertString(offset, "\n")
|
||||||
return EditorLine.Pointer.init(atPosition.line, this)
|
return atPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertText(atPosition: Offset, text: CharSequence) {
|
override fun insertText(atPosition: Int, text: CharSequence) {
|
||||||
editor.document.insertString(atPosition.point, text)
|
editor.document.insertString(atPosition, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceString(start: Int, end: Int, newString: String) {
|
override fun replaceString(start: Int, end: Int, newString: String) {
|
||||||
@ -128,13 +126,13 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 30.12.2021 Is end offset inclusive?
|
// TODO: 30.12.2021 Is end offset inclusive?
|
||||||
override fun getLineRange(line: EditorLine.Pointer): Pair<Offset, Offset> {
|
override fun getLineRange(line: Int): Pair<Int, Int> {
|
||||||
// TODO: 30.12.2021 getLineEndOffset returns the same value for "xyz" and "xyz\n"
|
// TODO: 30.12.2021 getLineEndOffset returns the same value for "xyz" and "xyz\n"
|
||||||
return editor.document.getLineStartOffset(line.line).offset to editor.document.getLineEndOffset(line.line).offset
|
return editor.document.getLineStartOffset(line) to editor.document.getLineEndOffset(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLine(offset: Offset): EditorLine.Pointer {
|
override fun getLine(offset: Int): Int {
|
||||||
return EditorLine.Pointer.init(editor.offsetToLogicalPosition(offset.point).line, this)
|
return editor.offsetToLogicalPosition(offset).line
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun carets(): List<VimCaret> {
|
override fun carets(): List<VimCaret> {
|
||||||
@ -203,15 +201,15 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
return editor.isOneLineMode
|
return editor.isOneLineMode
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getText(left: Offset, right: Offset): CharSequence {
|
override fun getText(left: Int, right: Int): CharSequence {
|
||||||
return editor.document.charsSequence.subSequence(left.point, right.point)
|
return editor.document.charsSequence.subSequence(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun search(
|
override fun search(
|
||||||
pair: Pair<Offset, Offset>,
|
pair: Pair<Int, Int>,
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
shiftType: LineDeleteShift,
|
shiftType: LineDeleteShift,
|
||||||
): Pair<Pair<Offset, Offset>, LineDeleteShift>? {
|
): Pair<Pair<Int, Int>, LineDeleteShift>? {
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
return when (shiftType) {
|
return when (shiftType) {
|
||||||
LineDeleteShift.NO_NL -> if (pair.noGuard(ijEditor)) return pair to shiftType else null
|
LineDeleteShift.NO_NL -> if (pair.noGuard(ijEditor)) return pair to shiftType else null
|
||||||
@ -358,10 +356,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
return EditorHelper.getVirtualFile(editor)?.getUrl()?.let { VirtualFileManager.extractProtocol(it) }
|
return EditorHelper.getVirtualFile(editor)?.getUrl()?.let { VirtualFileManager.extractProtocol(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val projectId = editor.project?.basePath ?: DEFAULT_PROJECT_ID
|
override val projectId = editor.project?.let { injector.file.getProjectId(it) } ?: DEFAULT_PROJECT_ID
|
||||||
|
|
||||||
override fun visualPositionToOffset(position: VimVisualPosition): Offset {
|
override fun visualPositionToOffset(position: VimVisualPosition): Int {
|
||||||
return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight)).offset
|
return editor.visualPositionToOffset(VisualPosition(position.line, position.column, position.leansRight))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun exitInsertMode(context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
@ -417,8 +415,8 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
return visualPosition.run { VimVisualPosition(line, column, leansRight) }
|
return visualPosition.run { VimVisualPosition(line, column, leansRight) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLiveMarker(start: Offset, end: Offset): LiveRange {
|
override fun createLiveMarker(start: Int, end: Int): LiveRange {
|
||||||
return editor.document.createRangeMarker(start.point, end.point).vim
|
return editor.document.createRangeMarker(start, end).vim
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,10 +454,10 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
ijFoldRegion.isExpanded = value
|
ijFoldRegion.isExpanded = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override val startOffset: Offset
|
override val startOffset: Int
|
||||||
get() = Offset(ijFoldRegion.startOffset)
|
get() = ijFoldRegion.startOffset
|
||||||
override val endOffset: Offset
|
override val endOffset: Int
|
||||||
get() = Offset(ijFoldRegion.endOffset)
|
get() = ijFoldRegion.endOffset
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,17 +466,17 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor() {
|
|||||||
return caret
|
return caret
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Pair<Offset, Offset>.noGuard(editor: Editor): Boolean {
|
private fun Pair<Int, Int>.noGuard(editor: Editor): Boolean {
|
||||||
return editor.document.getRangeGuard(this.first.point, this.second.point) == null
|
return editor.document.getRangeGuard(this.first, this.second) == null
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun Pair<Offset, Offset>.shift(
|
private inline fun Pair<Int, Int>.shift(
|
||||||
shiftStart: Int = 0,
|
shiftStart: Int = 0,
|
||||||
shiftEnd: Int = 0,
|
shiftEnd: Int = 0,
|
||||||
action: Pair<Offset, Offset>.() -> Unit,
|
action: Pair<Int, Int>.() -> Unit,
|
||||||
) {
|
) {
|
||||||
val data =
|
val data =
|
||||||
(this.first.point + shiftStart).coerceAtLeast(0).offset to (this.second.point + shiftEnd).coerceAtLeast(0).offset
|
(this.first + shiftStart).coerceAtLeast(0) to (this.second + shiftEnd).coerceAtLeast(0)
|
||||||
data.action()
|
data.action()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,3 +498,6 @@ public val Editor.vim: VimEditor
|
|||||||
get() = IjVimEditor(this)
|
get() = IjVimEditor(this)
|
||||||
public val VimEditor.ij: Editor
|
public val VimEditor.ij: Editor
|
||||||
get() = (this as IjVimEditor).editor
|
get() = (this as IjVimEditor).editor
|
||||||
|
|
||||||
|
public val com.intellij.openapi.util.TextRange.vim: TextRange
|
||||||
|
get() = TextRange(this.startOffset, this.endOffset)
|
||||||
|
@ -42,6 +42,7 @@ import com.maddyhome.idea.vim.api.VimMessages
|
|||||||
import com.maddyhome.idea.vim.api.VimMotionGroup
|
import com.maddyhome.idea.vim.api.VimMotionGroup
|
||||||
import com.maddyhome.idea.vim.api.VimOptionGroup
|
import com.maddyhome.idea.vim.api.VimOptionGroup
|
||||||
import com.maddyhome.idea.vim.api.VimProcessGroup
|
import com.maddyhome.idea.vim.api.VimProcessGroup
|
||||||
|
import com.maddyhome.idea.vim.api.VimPsiService
|
||||||
import com.maddyhome.idea.vim.api.VimRegexpService
|
import com.maddyhome.idea.vim.api.VimRegexpService
|
||||||
import com.maddyhome.idea.vim.api.VimScrollGroup
|
import com.maddyhome.idea.vim.api.VimScrollGroup
|
||||||
import com.maddyhome.idea.vim.api.VimSearchGroup
|
import com.maddyhome.idea.vim.api.VimSearchGroup
|
||||||
@ -65,6 +66,7 @@ import com.maddyhome.idea.vim.group.FileGroup
|
|||||||
import com.maddyhome.idea.vim.group.GlobalIjOptions
|
import com.maddyhome.idea.vim.group.GlobalIjOptions
|
||||||
import com.maddyhome.idea.vim.group.HistoryGroup
|
import com.maddyhome.idea.vim.group.HistoryGroup
|
||||||
import com.maddyhome.idea.vim.group.IjVimOptionGroup
|
import com.maddyhome.idea.vim.group.IjVimOptionGroup
|
||||||
|
import com.maddyhome.idea.vim.group.IjVimPsiService
|
||||||
import com.maddyhome.idea.vim.group.MacroGroup
|
import com.maddyhome.idea.vim.group.MacroGroup
|
||||||
import com.maddyhome.idea.vim.group.MotionGroup
|
import com.maddyhome.idea.vim.group.MotionGroup
|
||||||
import com.maddyhome.idea.vim.group.SearchGroup
|
import com.maddyhome.idea.vim.group.SearchGroup
|
||||||
@ -147,6 +149,8 @@ internal class IjVimInjector : VimInjectorBase() {
|
|||||||
get() = service<MacroGroup>()
|
get() = service<MacroGroup>()
|
||||||
override val undo: VimUndoRedo
|
override val undo: VimUndoRedo
|
||||||
get() = service<UndoRedoHelper>()
|
get() = service<UndoRedoHelper>()
|
||||||
|
override val psiService: VimPsiService
|
||||||
|
get() = service<IjVimPsiService>()
|
||||||
override val commandLineHelper: VimCommandLineHelper
|
override val commandLineHelper: VimCommandLineHelper
|
||||||
get() = service<CommandLineHelper>()
|
get() = service<CommandLineHelper>()
|
||||||
override val nativeActionManager: NativeActionManager
|
override val nativeActionManager: NativeActionManager
|
||||||
|
@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.newapi
|
|||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.Options
|
import com.maddyhome.idea.vim.api.Options
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@ -26,9 +27,7 @@ import com.maddyhome.idea.vim.helper.shouldIgnoreCase
|
|||||||
import com.maddyhome.idea.vim.helper.updateSearchHighlights
|
import com.maddyhome.idea.vim.helper.updateSearchHighlights
|
||||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
|
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener
|
||||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler
|
import com.maddyhome.idea.vim.vimscript.model.functions.handlers.SubmatchFunctionHandler
|
||||||
import com.maddyhome.idea.vim.vimscript.parser.VimscriptParser.parseExpression
|
|
||||||
import org.jetbrains.annotations.TestOnly
|
import org.jetbrains.annotations.TestOnly
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
@ -82,6 +81,7 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
|
|||||||
|
|
||||||
override fun confirmChoice(
|
override fun confirmChoice(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
match: String,
|
match: String,
|
||||||
caret: VimCaret,
|
caret: VimCaret,
|
||||||
startOffset: Int,
|
startOffset: Int,
|
||||||
@ -121,7 +121,6 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
|
|||||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
||||||
val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
|
val exEntryPanel: com.maddyhome.idea.vim.ui.ex.ExEntryPanel =
|
||||||
com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
|
com.maddyhome.idea.vim.ui.ex.ExEntryPanel.getInstanceWithoutShortcuts()
|
||||||
val context = injector.executionContextManager.onEditor(editor, null)
|
|
||||||
exEntryPanel.activate(
|
exEntryPanel.activate(
|
||||||
editor.ij,
|
editor.ij,
|
||||||
(context as IjEditorExecutionContext).context,
|
(context as IjEditorExecutionContext).context,
|
||||||
@ -136,10 +135,6 @@ public abstract class IjVimSearchGroup : VimSearchGroupBase() {
|
|||||||
return result.get()
|
return result.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun parseVimScriptExpression(expressionString: String): Expression? {
|
|
||||||
return parseExpression(expressionString)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
|
override fun addSubstitutionConfirmationHighlight(editor: VimEditor, startOffset: Int, endOffset: Int) {
|
||||||
val hl = addSubstitutionConfirmationHighlight(
|
val hl = addSubstitutionConfirmationHighlight(
|
||||||
(editor as IjVimEditor).editor,
|
(editor as IjVimEditor).editor,
|
||||||
|
@ -13,144 +13,31 @@ import com.intellij.openapi.diagnostic.Logger
|
|||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimSearchHelperBase
|
import com.maddyhome.idea.vim.api.VimSearchHelperBase
|
||||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
|
||||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
|
||||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
|
||||||
import com.maddyhome.idea.vim.common.Direction
|
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.helper.CharacterHelper
|
|
||||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
|
||||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||||
import com.maddyhome.idea.vim.helper.SearchOptions
|
import com.maddyhome.idea.vim.helper.SearchOptions
|
||||||
import com.maddyhome.idea.vim.helper.checkInString
|
|
||||||
import com.maddyhome.idea.vim.helper.fileSize
|
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntComparator
|
import it.unimi.dsi.fastutil.ints.IntComparator
|
||||||
import it.unimi.dsi.fastutil.ints.IntComparators
|
import it.unimi.dsi.fastutil.ints.IntComparators
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Function
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.max
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
internal class IjVimSearchHelper : VimSearchHelperBase() {
|
internal class IjVimSearchHelper : VimSearchHelperBase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BLOCK_CHARS = "{}()[]<>"
|
|
||||||
private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name)
|
private val logger = Logger.getInstance(IjVimSearchHelper::class.java.name)
|
||||||
}
|
}
|
||||||
override fun findSection(
|
|
||||||
editor: VimEditor,
|
|
||||||
caret: ImmutableVimCaret,
|
|
||||||
type: Char,
|
|
||||||
direction: Int,
|
|
||||||
count: Int,
|
|
||||||
)
|
|
||||||
: Int {
|
|
||||||
val documentText: CharSequence = editor.ij.document.charsSequence
|
|
||||||
var currentLine: Int = caret.ij.logicalPosition.line + direction
|
|
||||||
var resultOffset = -1
|
|
||||||
var remainingTargets = count
|
|
||||||
|
|
||||||
while (currentLine in 1 until editor.lineCount() && remainingTargets > 0) {
|
|
||||||
val lineStartOffset = editor.getLineStartOffset(currentLine)
|
|
||||||
if (lineStartOffset < documentText.length) {
|
|
||||||
val currentChar = documentText[lineStartOffset]
|
|
||||||
if (currentChar == type || currentChar == '\u000C') {
|
|
||||||
resultOffset = lineStartOffset
|
|
||||||
remainingTargets--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentLine += direction
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resultOffset == -1) {
|
|
||||||
resultOffset = if (direction < 0) 0 else documentText.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultOffset
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
override fun findMethodEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
||||||
|
// TODO add it to PsiService
|
||||||
return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count)
|
return PsiHelper.findMethodEnd(editor.ij, caret.ij.offset, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
override fun findMethodStart(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
||||||
|
// TODO add it to PsiService
|
||||||
return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count)
|
return PsiHelper.findMethodStart(editor.ij, caret.ij.offset, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findUnmatchedBlock(editor: VimEditor, caret: ImmutableVimCaret, type: Char, count: Int): Int {
|
|
||||||
val chars: CharSequence = editor.ij.document.charsSequence
|
|
||||||
var pos: Int = caret.ij.offset
|
|
||||||
val loc = BLOCK_CHARS.indexOf(type)
|
|
||||||
// What direction should we go now (-1 is backward, 1 is forward)
|
|
||||||
val dir = if (loc % 2 == 0) Direction.BACKWARDS else Direction.FORWARDS
|
|
||||||
// Which character did we find and which should we now search for
|
|
||||||
val match = BLOCK_CHARS[loc]
|
|
||||||
val found = BLOCK_CHARS[loc - dir.toInt()]
|
|
||||||
|
|
||||||
if (pos < chars.length && chars[pos] == type) {
|
|
||||||
pos += dir.toInt()
|
|
||||||
}
|
|
||||||
return findBlockLocation(chars, found, match, dir, pos, count)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findBlockLocation(
|
|
||||||
chars: CharSequence,
|
|
||||||
found: Char,
|
|
||||||
match: Char,
|
|
||||||
dir: Direction,
|
|
||||||
pos: Int,
|
|
||||||
cnt: Int,
|
|
||||||
): Int {
|
|
||||||
var position = pos
|
|
||||||
var count = cnt
|
|
||||||
var res = -1
|
|
||||||
val initialPos = position
|
|
||||||
val initialInString = checkInString(chars, position, true)
|
|
||||||
val inCheckPosF =
|
|
||||||
Function { x: Int -> if (dir === Direction.BACKWARDS && x > 0) x - 1 else x + 1 }
|
|
||||||
val inCheckPos = inCheckPosF.apply(position)
|
|
||||||
var inString = checkInString(chars, inCheckPos, true)
|
|
||||||
var inChar = checkInString(chars, inCheckPos, false)
|
|
||||||
var stack = 0
|
|
||||||
// Search to start or end of file, as appropriate
|
|
||||||
val charsToSearch: Set<Char> = HashSet(listOf('\'', '"', '\n', match, found))
|
|
||||||
while (position >= 0 && position < chars.length && count > 0) {
|
|
||||||
val (c, second) = SearchHelper.findPositionOfFirstCharacter(chars, position, charsToSearch, true, dir) ?: return -1
|
|
||||||
position = second
|
|
||||||
// If we found a match and we're not in a string...
|
|
||||||
if (c == match && (!inString) && !inChar) {
|
|
||||||
// We found our match
|
|
||||||
if (stack == 0) {
|
|
||||||
res = position
|
|
||||||
count--
|
|
||||||
} else {
|
|
||||||
stack--
|
|
||||||
}
|
|
||||||
} else if (c == '\n') {
|
|
||||||
inString = false
|
|
||||||
inChar = false
|
|
||||||
} else if (position != initialPos) {
|
|
||||||
// We found another character like our original - belongs to another pair
|
|
||||||
if (!inString && !inChar && c == found) {
|
|
||||||
stack++
|
|
||||||
} else if (!inChar) {
|
|
||||||
inString = checkInString(chars, inCheckPosF.apply(position), true)
|
|
||||||
} else if (!inString) {
|
|
||||||
inChar = checkInString(chars, inCheckPosF.apply(position), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
position += dir.toInt()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findPattern(
|
override fun findPattern(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
pattern: String?,
|
pattern: String?,
|
||||||
@ -173,525 +60,6 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
|
|||||||
else SearchHelper.findAll(editor.ij, pattern, startLine, endLine, ignoreCase)
|
else SearchHelper.findAll(editor.ij, pattern, startLine, endLine, ignoreCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findNextCharacterOnLine(editor: VimEditor, caret: ImmutableVimCaret, count: Int, ch: Char): Int {
|
|
||||||
val line: Int = caret.ij.logicalPosition.line
|
|
||||||
val start = editor.getLineStartOffset(line)
|
|
||||||
val end = editor.getLineEndOffset(line, true)
|
|
||||||
val chars: CharSequence = editor.ij.document.charsSequence
|
|
||||||
var found = 0
|
|
||||||
val step = if (count >= 0) 1 else -1
|
|
||||||
var pos: Int = caret.ij.offset + step
|
|
||||||
while (pos in start until end && pos < chars.length) {
|
|
||||||
if (chars[pos] == ch) {
|
|
||||||
found++
|
|
||||||
if (found == abs(count)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += step
|
|
||||||
}
|
|
||||||
|
|
||||||
return if (found == abs(count)) {
|
|
||||||
pos
|
|
||||||
} else {
|
|
||||||
-1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findWordUnderCursor(
|
|
||||||
editor: VimEditor,
|
|
||||||
caret: ImmutableVimCaret,
|
|
||||||
count: Int,
|
|
||||||
dir: Int,
|
|
||||||
isOuter: Boolean,
|
|
||||||
isBig: Boolean,
|
|
||||||
hasSelection: Boolean,
|
|
||||||
): TextRange {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("count=$count")
|
|
||||||
logger.debug("dir=$dir")
|
|
||||||
logger.debug("isOuter=$isOuter")
|
|
||||||
logger.debug("isBig=$isBig")
|
|
||||||
logger.debug("hasSelection=$hasSelection")
|
|
||||||
}
|
|
||||||
|
|
||||||
val chars: CharSequence = editor.ij.document.charsSequence
|
|
||||||
//int min = EditorHelper.getLineStartOffset(editor, EditorHelper.getCurrentLogicalLine(editor));
|
|
||||||
//int max = EditorHelper.getLineEndOffset(editor, EditorHelper.getCurrentLogicalLine(editor), true);
|
|
||||||
val min = 0
|
|
||||||
val max: Int = editor.ij.fileSize
|
|
||||||
if (max == 0) return TextRange(0, 0)
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("min=$min")
|
|
||||||
logger.debug("max=$max")
|
|
||||||
}
|
|
||||||
|
|
||||||
val pos: Int = caret.ij.offset
|
|
||||||
if (chars.length <= pos) return TextRange(chars.length - 1, chars.length - 1)
|
|
||||||
|
|
||||||
val startSpace = charType(editor, chars[pos], isBig) === CharacterHelper.CharacterType.WHITESPACE
|
|
||||||
// Find word start
|
|
||||||
val onWordStart = pos == min ||
|
|
||||||
charType(editor, chars[pos - 1], isBig) !==
|
|
||||||
charType(editor, chars[pos], isBig)
|
|
||||||
var start = pos
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("pos=$pos")
|
|
||||||
logger.debug("onWordStart=$onWordStart")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!onWordStart && !(startSpace && isOuter) || hasSelection || count > 1 && dir == -1) {
|
|
||||||
start = if (dir == 1) {
|
|
||||||
findNextWord(editor, pos, -1, isBig, !isOuter)
|
|
||||||
} else {
|
|
||||||
findNextWord(
|
|
||||||
editor,
|
|
||||||
pos,
|
|
||||||
-(count - if (onWordStart && !hasSelection) 1 else 0),
|
|
||||||
isBig,
|
|
||||||
!isOuter
|
|
||||||
)
|
|
||||||
}
|
|
||||||
start = editor.normalizeOffset(start, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) logger.debug("start=$start")
|
|
||||||
|
|
||||||
// Find word end
|
|
||||||
|
|
||||||
// Find word end
|
|
||||||
val onWordEnd = pos >= max - 1 ||
|
|
||||||
charType(editor, chars[pos + 1], isBig) !==
|
|
||||||
charType(editor, chars[pos], isBig)
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) logger.debug("onWordEnd=$onWordEnd")
|
|
||||||
|
|
||||||
var end = pos
|
|
||||||
if (!onWordEnd || hasSelection || count > 1 && dir == 1 || startSpace && isOuter) {
|
|
||||||
end = if (dir == 1) {
|
|
||||||
val c = count - if (onWordEnd && !hasSelection && (!(startSpace && isOuter) || startSpace && !isOuter)) 1 else 0
|
|
||||||
findNextWordEnd(editor, pos, c, isBig, !isOuter)
|
|
||||||
} else {
|
|
||||||
findNextWordEnd(editor, pos, 1, isBig, !isOuter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) logger.debug("end=$end")
|
|
||||||
|
|
||||||
var goBack = startSpace && !hasSelection || !startSpace && hasSelection && !onWordStart
|
|
||||||
if (dir == 1 && isOuter) {
|
|
||||||
var firstEnd = end
|
|
||||||
if (count > 1) {
|
|
||||||
firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
|
|
||||||
}
|
|
||||||
if (firstEnd < max - 1) {
|
|
||||||
if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
|
|
||||||
goBack = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dir == -1 && isOuter && startSpace) {
|
|
||||||
if (pos > min) {
|
|
||||||
if (charType(editor, chars[pos - 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
|
|
||||||
goBack = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var goForward = dir == 1 && isOuter && (!startSpace && !onWordEnd || startSpace && onWordEnd && hasSelection)
|
|
||||||
if (!goForward && dir == 1 && isOuter) {
|
|
||||||
var firstEnd = end
|
|
||||||
if (count > 1) {
|
|
||||||
firstEnd = findNextWordEnd(editor, pos, 1, isBig, false)
|
|
||||||
}
|
|
||||||
if (firstEnd < max - 1) {
|
|
||||||
if (charType(editor, chars[firstEnd + 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
|
|
||||||
goForward = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!goForward && dir == 1 && isOuter && !startSpace && !hasSelection) {
|
|
||||||
if (end < max - 1) {
|
|
||||||
if (charType(editor, chars[end + 1], !isBig) !==
|
|
||||||
charType(editor, chars[end], !isBig)
|
|
||||||
) {
|
|
||||||
goForward = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("goBack=$goBack")
|
|
||||||
logger.debug("goForward=$goForward")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (goForward) {
|
|
||||||
if (editor.anyNonWhitespace(end, 1)) {
|
|
||||||
while (end + 1 < max &&
|
|
||||||
charType(editor, chars[end + 1], false) === CharacterHelper.CharacterType.WHITESPACE
|
|
||||||
) {
|
|
||||||
end++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (goBack) {
|
|
||||||
if (editor.anyNonWhitespace(start, -1)) {
|
|
||||||
while (start > min &&
|
|
||||||
charType(editor, chars[start - 1], false) === CharacterHelper.CharacterType.WHITESPACE
|
|
||||||
) {
|
|
||||||
start--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("start=$start")
|
|
||||||
logger.debug("end=$end")
|
|
||||||
}
|
|
||||||
|
|
||||||
// End offset is exclusive
|
|
||||||
return TextRange(start, end + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findBlockTagRange(editor: VimEditor, caret: ImmutableVimCaret, count: Int, isOuter: Boolean): TextRange? {
|
|
||||||
var counter = count
|
|
||||||
var isOuterVariable = isOuter
|
|
||||||
val position: Int = caret.ij.offset
|
|
||||||
val sequence: CharSequence = editor.ij.document.charsSequence
|
|
||||||
|
|
||||||
val selectionStart: Int = caret.ij.selectionStart
|
|
||||||
val selectionEnd: Int = caret.ij.selectionEnd
|
|
||||||
|
|
||||||
val isRangeSelection = selectionEnd - selectionStart > 1
|
|
||||||
|
|
||||||
var searchStartPosition: Int
|
|
||||||
searchStartPosition = if (!isRangeSelection) {
|
|
||||||
val line: Int = caret.ij.logicalPosition.line
|
|
||||||
val lineBegin: Int = editor.ij.document.getLineStartOffset(line)
|
|
||||||
ignoreWhitespaceAtLineStart(sequence, lineBegin, position)
|
|
||||||
} else {
|
|
||||||
selectionEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInHTMLTag(sequence, searchStartPosition, false)) {
|
|
||||||
// caret is inside opening tag. Move to closing '>'.
|
|
||||||
while (searchStartPosition < sequence.length && sequence[searchStartPosition] != '>') {
|
|
||||||
searchStartPosition++
|
|
||||||
}
|
|
||||||
} else if (isInHTMLTag(sequence, searchStartPosition, true)) {
|
|
||||||
// caret is inside closing tag. Move to starting '<'.
|
|
||||||
while (searchStartPosition > 0 && sequence[searchStartPosition] != '<') {
|
|
||||||
searchStartPosition--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
val (closingTagTextRange, tagName) = findUnmatchedClosingTag(sequence, searchStartPosition, counter)
|
|
||||||
?: return null
|
|
||||||
val openingTag = findUnmatchedOpeningTag(sequence, closingTagTextRange.startOffset, tagName)
|
|
||||||
?: return null
|
|
||||||
if (isRangeSelection && openingTag.endOffset - 1 >= selectionStart) {
|
|
||||||
// If there was already some text selected and the new selection would not extend further, we try again
|
|
||||||
searchStartPosition = closingTagTextRange.endOffset
|
|
||||||
counter = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var selectionEndWithoutNewline = selectionEnd
|
|
||||||
while (selectionEndWithoutNewline < sequence.length && sequence[selectionEndWithoutNewline] == '\n') {
|
|
||||||
selectionEndWithoutNewline++
|
|
||||||
}
|
|
||||||
val mode = getInstance(editor).mode
|
|
||||||
if (mode is VISUAL) {
|
|
||||||
if (closingTagTextRange.startOffset == selectionEndWithoutNewline &&
|
|
||||||
openingTag.endOffset == selectionStart
|
|
||||||
) {
|
|
||||||
// Special case: if the inner tag is already selected we should like isOuter is active
|
|
||||||
// Note that we need to ignore newlines, because their selection is lost between multiple "it" invocations
|
|
||||||
isOuterVariable = true
|
|
||||||
} else if (openingTag.endOffset == closingTagTextRange.startOffset &&
|
|
||||||
selectionStart == openingTag.endOffset
|
|
||||||
) {
|
|
||||||
// Special case: for an empty tag pair (e.g. <a></a>) the whole tag is selected if the caret is in the middle.
|
|
||||||
isOuterVariable = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (isOuterVariable) {
|
|
||||||
TextRange(openingTag.startOffset, closingTagTextRange.endOffset)
|
|
||||||
} else {
|
|
||||||
TextRange(openingTag.endOffset, closingTagTextRange.startOffset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns new position which ignore whitespaces at beginning of the line
|
|
||||||
*/
|
|
||||||
private fun ignoreWhitespaceAtLineStart(seq: CharSequence, lineStart: Int, pos: Int): Int {
|
|
||||||
var position = pos
|
|
||||||
if (seq.subSequence(lineStart, position).chars().allMatch { codePoint: Int ->
|
|
||||||
Character.isWhitespace(
|
|
||||||
codePoint
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
while (position < seq.length && seq[position] != '\n' && Character.isWhitespace(seq[position])) {
|
|
||||||
position++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if there is a html at the given position. Ignores tags with a trailing slash like <aaa></aaa>.
|
|
||||||
*/
|
|
||||||
private fun isInHTMLTag(sequence: CharSequence, position: Int, isEndtag: Boolean): Boolean {
|
|
||||||
var openingBracket = -1
|
|
||||||
run {
|
|
||||||
var i = position
|
|
||||||
while (i >= 0 && i < sequence.length) {
|
|
||||||
if (sequence[i] == '<') {
|
|
||||||
openingBracket = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (sequence[i] == '>' && i != position) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (openingBracket == -1) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val hasSlashAfterOpening = openingBracket + 1 < sequence.length && sequence[openingBracket + 1] == '/'
|
|
||||||
if (isEndtag && !hasSlashAfterOpening || !isEndtag && hasSlashAfterOpening) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var closingBracket = -1
|
|
||||||
for (i in openingBracket until sequence.length) {
|
|
||||||
if (sequence[i] == '>') {
|
|
||||||
closingBracket = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return closingBracket != -1 && sequence[closingBracket - 1] != '/'
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findUnmatchedOpeningTag(
|
|
||||||
sequence: CharSequence,
|
|
||||||
position: Int,
|
|
||||||
tagName: String,
|
|
||||||
): TextRange? {
|
|
||||||
val quotedTagName = Pattern.quote(tagName)
|
|
||||||
val patternString = ("(</%s>)" // match closing tags
|
|
||||||
+
|
|
||||||
"|(<%s" // or opening tags starting with tagName
|
|
||||||
+
|
|
||||||
"(\\s([^>]*" // After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
|
|
||||||
+
|
|
||||||
"[^/])?)?>)") // Slash is not allowed as last character (this would be a self closing tag).
|
|
||||||
val tagPattern =
|
|
||||||
Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE)
|
|
||||||
val matcher = tagPattern.matcher(sequence.subSequence(0, position + 1))
|
|
||||||
val openTags: Deque<TextRange> = ArrayDeque()
|
|
||||||
while (matcher.find()) {
|
|
||||||
val match = TextRange(matcher.start(), matcher.end())
|
|
||||||
if (sequence[matcher.start() + 1] == '/') {
|
|
||||||
if (!openTags.isEmpty()) {
|
|
||||||
openTags.pop()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
openTags.push(match)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return if (openTags.isEmpty()) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
openTags.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findUnmatchedClosingTag(
|
|
||||||
sequence: CharSequence,
|
|
||||||
position: Int,
|
|
||||||
count: Int,
|
|
||||||
): Pair<TextRange, String>? {
|
|
||||||
// The tag name may contain any characters except slashes, whitespace and '>'
|
|
||||||
var counter = count
|
|
||||||
val tagNamePattern = "([^/\\s>]+)"
|
|
||||||
// An opening tag consists of '<' followed by a tag name, optionally some additional text after whitespace and a '>'
|
|
||||||
val openingTagPattern = String.format("<%s(?:\\s[^>]*)?>", tagNamePattern)
|
|
||||||
val closingTagPattern = String.format("</%s>", tagNamePattern)
|
|
||||||
val tagPattern = Pattern.compile(String.format("(?:%s)|(?:%s)", openingTagPattern, closingTagPattern))
|
|
||||||
val matcher = tagPattern.matcher(sequence.subSequence(position, sequence.length))
|
|
||||||
val openTags: Deque<String> = ArrayDeque()
|
|
||||||
while (matcher.find()) {
|
|
||||||
val isClosingTag = matcher.group(1) == null
|
|
||||||
if (isClosingTag) {
|
|
||||||
val tagName = matcher.group(2)
|
|
||||||
// Ignore unmatched open tags. Either the file is malformed or it might be a tag like <br> that does not need to be closed.
|
|
||||||
while (!openTags.isEmpty() && !openTags.peek().equals(tagName, ignoreCase = true)) {
|
|
||||||
openTags.pop()
|
|
||||||
}
|
|
||||||
if (openTags.isEmpty()) {
|
|
||||||
if (counter <= 1) {
|
|
||||||
return Pair(TextRange(position + matcher.start(), position + matcher.end()), tagName)
|
|
||||||
} else {
|
|
||||||
counter--
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
openTags.pop()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val tagName = matcher.group(1)
|
|
||||||
openTags.push(tagName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findBlockRange(
|
|
||||||
editor: VimEditor,
|
|
||||||
caret: ImmutableVimCaret,
|
|
||||||
type: Char,
|
|
||||||
count: Int,
|
|
||||||
isOuter: Boolean,
|
|
||||||
): TextRange? {
|
|
||||||
val chars: CharSequence = editor.ij.document.charsSequence
|
|
||||||
var pos: Int = caret.ij.offset
|
|
||||||
var start: Int = caret.ij.selectionStart
|
|
||||||
var end: Int = caret.ij.selectionEnd
|
|
||||||
|
|
||||||
val loc = BLOCK_CHARS.indexOf(type)
|
|
||||||
val close = BLOCK_CHARS[loc + 1]
|
|
||||||
|
|
||||||
// extend the range for blank line after type and before close, as they are excluded when inner match
|
|
||||||
if (!isOuter) {
|
|
||||||
if (start > 1 && chars[start - 2] == type && chars[start - 1] == '\n') {
|
|
||||||
start--
|
|
||||||
}
|
|
||||||
if (end < chars.length && chars[end] == '\n') {
|
|
||||||
var isSingleLineAllWhiteSpaceUntilClose = false
|
|
||||||
var countWhiteSpaceCharacter = 1
|
|
||||||
while (end + countWhiteSpaceCharacter < chars.length) {
|
|
||||||
if (Character.isWhitespace(chars[end + countWhiteSpaceCharacter]) &&
|
|
||||||
chars[end + countWhiteSpaceCharacter] != '\n'
|
|
||||||
) {
|
|
||||||
countWhiteSpaceCharacter++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (chars[end + countWhiteSpaceCharacter] == close) {
|
|
||||||
isSingleLineAllWhiteSpaceUntilClose = true
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (isSingleLineAllWhiteSpaceUntilClose) {
|
|
||||||
end += countWhiteSpaceCharacter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var rangeSelection = end - start > 1
|
|
||||||
if (rangeSelection && start == 0) // early return not only for optimization
|
|
||||||
{
|
|
||||||
return null // but also not to break the interval semantic on this edge case (see below)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* In case of successive inner selection. We want to break out of
|
|
||||||
* the block delimiter of the current inner selection.
|
|
||||||
* In other terms, for the rest of the algorithm, a previous inner selection of a block
|
|
||||||
* if equivalent to an outer one. */
|
|
||||||
|
|
||||||
/* In case of successive inner selection. We want to break out of
|
|
||||||
* the block delimiter of the current inner selection.
|
|
||||||
* In other terms, for the rest of the algorithm, a previous inner selection of a block
|
|
||||||
* if equivalent to an outer one. */if (!isOuter && start - 1 >= 0 && type == chars[start - 1] && end < chars.length && close == chars[end]) {
|
|
||||||
start -= 1
|
|
||||||
pos = start
|
|
||||||
rangeSelection = true
|
|
||||||
}
|
|
||||||
|
|
||||||
/* when one char is selected, we want to find the enclosing block of (start,end]
|
|
||||||
* although when a range of characters is selected, we want the enclosing block of [start, end]
|
|
||||||
* shifting the position allow to express which kind of interval we work on */
|
|
||||||
|
|
||||||
/* when one char is selected, we want to find the enclosing block of (start,end]
|
|
||||||
* although when a range of characters is selected, we want the enclosing block of [start, end]
|
|
||||||
* shifting the position allow to express which kind of interval we work on */if (rangeSelection) pos =
|
|
||||||
max(0.0, (start - 1).toDouble()).toInt()
|
|
||||||
|
|
||||||
val initialPosIsInString = checkInString(chars, pos, true)
|
|
||||||
|
|
||||||
var bstart = -1
|
|
||||||
var bend = -1
|
|
||||||
|
|
||||||
var startPosInStringFound = false
|
|
||||||
|
|
||||||
if (initialPosIsInString) {
|
|
||||||
val quoteRange = injector.searchHelper
|
|
||||||
.findBlockQuoteInLineRange(editor, caret, '"', false)
|
|
||||||
if (quoteRange != null) {
|
|
||||||
val startOffset = quoteRange.startOffset
|
|
||||||
val endOffset = quoteRange.endOffset
|
|
||||||
val subSequence = chars.subSequence(startOffset, endOffset)
|
|
||||||
val inQuotePos = pos - startOffset
|
|
||||||
var inQuoteStart =
|
|
||||||
findBlockLocation(subSequence, close, type, Direction.BACKWARDS, inQuotePos, count)
|
|
||||||
if (inQuoteStart == -1) {
|
|
||||||
inQuoteStart =
|
|
||||||
findBlockLocation(subSequence, close, type, Direction.FORWARDS, inQuotePos, count)
|
|
||||||
}
|
|
||||||
if (inQuoteStart != -1) {
|
|
||||||
startPosInStringFound = true
|
|
||||||
val inQuoteEnd =
|
|
||||||
findBlockLocation(subSequence, type, close, Direction.FORWARDS, inQuoteStart, 1)
|
|
||||||
if (inQuoteEnd != -1) {
|
|
||||||
bstart = inQuoteStart + startOffset
|
|
||||||
bend = inQuoteEnd + startOffset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!startPosInStringFound) {
|
|
||||||
bstart = findBlockLocation(chars, close, type, Direction.BACKWARDS, pos, count)
|
|
||||||
if (bstart == -1) {
|
|
||||||
bstart = findBlockLocation(chars, close, type, Direction.FORWARDS, pos, count)
|
|
||||||
}
|
|
||||||
if (bstart != -1) {
|
|
||||||
bend = findBlockLocation(chars, type, close, Direction.FORWARDS, bstart, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bstart == -1 || bend == -1) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isOuter) {
|
|
||||||
bstart++
|
|
||||||
// exclude first line break after start for inner match
|
|
||||||
if (chars[bstart] == '\n') {
|
|
||||||
bstart++
|
|
||||||
}
|
|
||||||
val o = editor.getLineStartForOffset(bend)
|
|
||||||
var allWhite = true
|
|
||||||
for (i in o until bend) {
|
|
||||||
if (!Character.isWhitespace(chars[i])) {
|
|
||||||
allWhite = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allWhite) {
|
|
||||||
bend = o - 2
|
|
||||||
} else {
|
|
||||||
bend--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// End offset exclusive
|
|
||||||
return TextRange(bstart, bend + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
override fun findMisspelledWord(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Int {
|
||||||
val startOffset: Int
|
val startOffset: Int
|
||||||
val endOffset: Int
|
val endOffset: Int
|
||||||
@ -700,17 +68,18 @@ internal class IjVimSearchHelper : VimSearchHelperBase() {
|
|||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
startOffset = 0
|
startOffset = 0
|
||||||
endOffset = caret.offset.point - 1
|
endOffset = caret.offset - 1
|
||||||
skipCount = -count - 1
|
skipCount = -count - 1
|
||||||
offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
|
offsetOrdering = IntComparators.OPPOSITE_COMPARATOR
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
startOffset = caret.offset.point + 1
|
startOffset = caret.offset + 1
|
||||||
endOffset = editor.ij.document.textLength
|
endOffset = editor.ij.document.textLength
|
||||||
skipCount = count - 1
|
skipCount = count - 1
|
||||||
offsetOrdering = IntComparators.NATURAL_COMPARATOR
|
offsetOrdering = IntComparators.NATURAL_COMPARATOR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add it to PsiService
|
||||||
return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
|
return SearchHelper.findMisspelledWords(editor.ij, startOffset, endOffset, skipCount, offsetOrdering)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ public class ExOutputPanel extends JPanel {
|
|||||||
KeyHandler.getInstance().getKeyStack().dump());
|
KeyHandler.getInstance().getKeyStack().dump());
|
||||||
}
|
}
|
||||||
KeyHandler.getInstance().getKeyStack().addKeys(keys);
|
KeyHandler.getInstance().getKeyStack().addKeys(keys);
|
||||||
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(myEditor), null);
|
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(myEditor));
|
||||||
VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
|
VimPlugin.getMacro().playbackKeys(new IjVimEditor(myEditor), context, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.ui.ex
|
|||||||
import com.intellij.openapi.diagnostic.debug
|
import com.intellij.openapi.diagnostic.debug
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.event.ActionEvent
|
import java.awt.event.ActionEvent
|
||||||
@ -126,12 +125,7 @@ internal object ExEditorKit : DefaultEditorKit() {
|
|||||||
val entry = ExEntryPanel.getInstance().entry
|
val entry = ExEntryPanel.getInstance().entry
|
||||||
val editor = entry.editor
|
val editor = entry.editor
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
keyHandler.handleKey(
|
keyHandler.handleKey(editor.vim, key, entry.context.vim, keyHandler.keyHandlerState)
|
||||||
editor.vim,
|
|
||||||
key,
|
|
||||||
injector.executionContextManager.onEditor(editor.vim, entry.context.vim),
|
|
||||||
keyHandler.keyHandlerState,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
|
val event = ActionEvent(e.source, e.id, c.toString(), e.getWhen(), e.modifiers)
|
||||||
super.actionPerformed(event)
|
super.actionPerformed(event)
|
||||||
|
@ -13,7 +13,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent
|
|||||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
|
import com.intellij.openapi.actionSystem.KeyboardShortcut
|
||||||
import com.intellij.openapi.project.DumbAwareAction
|
import com.intellij.openapi.project.DumbAwareAction
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
@ -37,12 +36,12 @@ internal class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : Dum
|
|||||||
if (keyStroke != null) {
|
if (keyStroke != null) {
|
||||||
val editor = exEntryPanel.entry.editor
|
val editor = exEntryPanel.entry.editor
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
keyHandler.handleKey(
|
|
||||||
editor.vim,
|
// About the context: we use the context of the main editor to execute actions on it.
|
||||||
keyStroke,
|
// e.dataContext will refer to the ex-entry editor and commands will be executed on it,
|
||||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
// thus it should not be used. For example, `:action EditorSelectWord` will not work with this context
|
||||||
keyHandler.keyHandlerState
|
val mainEditorContext = exEntryPanel.entry.context.vim
|
||||||
)
|
keyHandler.handleKey(editor.vim, keyStroke, mainEditorContext, keyHandler.keyHandlerState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,7 @@ internal class Executor : VimScriptExecutorBase() {
|
|||||||
VimPlugin.indicateError()
|
VimPlugin.indicateError()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.warn("Caught: ${e.message}")
|
logger.warn(e)
|
||||||
logger.warn(e.stackTrace.toString())
|
|
||||||
if (injector.application.isUnitTest()) {
|
if (injector.application.isUnitTest()) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
|
@ -113,9 +113,9 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (globalBusy) {
|
if (globalBusy) {
|
||||||
val match = regex.findInLine(editor, editor.currentCaret().getLine().line)
|
val match = regex.findInLine(editor, editor.currentCaret().getLine())
|
||||||
if (match is VimMatchResult.Success == !invert) {
|
if (match is VimMatchResult.Success == !invert) {
|
||||||
globalExecuteOne(editor, context, editor.getLineStartOffset(editor.currentCaret().getLine().line), cmd.toString())
|
globalExecuteOne(editor, context, editor.getLineStartOffset(editor.currentCaret().getLine()), cmd.toString())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val line1 = range.startLine
|
val line1 = range.startLine
|
||||||
@ -164,8 +164,8 @@ internal data class GlobalCommand(val ranges: Ranges, val argument: String, val
|
|||||||
val searchcol = 0
|
val searchcol = 0
|
||||||
if (globalBusy) {
|
if (globalBusy) {
|
||||||
val offset = editor.currentCaret().offset
|
val offset = editor.currentCaret().offset
|
||||||
val lineStartOffset = editor.getLineStartForOffset(offset.point)
|
val lineStartOffset = editor.getLineStartForOffset(offset)
|
||||||
match = sp.vim_regexec_multi(regmatch, editor, lcount, editor.currentCaret().getLine().line, searchcol)
|
match = sp.vim_regexec_multi(regmatch, editor, lcount, editor.currentCaret().getLine(), searchcol)
|
||||||
if ((!invert && match > 0) || (invert && match <= 0)) {
|
if ((!invert && match > 0) || (invert && match <= 0)) {
|
||||||
globalExecuteOne(editor, context, lineStartOffset, cmd.toString())
|
globalExecuteOne(editor, context, lineStartOffset, cmd.toString())
|
||||||
}
|
}
|
||||||
|
@ -136,11 +136,7 @@
|
|||||||
|
|
||||||
<!-- IdeaVim extensions-->
|
<!-- IdeaVim extensions-->
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<projectService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdDispatcher"/>
|
||||||
<postStartupActivity implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$NerdStartupActivity"/>
|
<applicationInitializedListener implementation="com.maddyhome.idea.vim.extension.nerdtree.NerdTreeApplicationListener"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
<projectListeners>
|
|
||||||
<listener class="com.maddyhome.idea.vim.extension.nerdtree.NerdTree$ProjectViewListener"
|
|
||||||
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
|
|
||||||
</projectListeners>
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@ -15,6 +15,7 @@ import com.maddyhome.idea.vim.state.mode.Mode
|
|||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
|
import org.jetbrains.plugins.ideavim.VimBehaviorDiffers
|
||||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
@ -381,10 +382,14 @@ class MotionActionTest : VimTestCase() {
|
|||||||
|
|
||||||
// VIM-1287 |d| |v_i{|
|
// VIM-1287 |d| |v_i{|
|
||||||
@Test
|
@Test
|
||||||
|
@VimBehaviorDiffers(
|
||||||
|
originalVimAfter = "{\"{foo, ${c}bar\", baz}}",
|
||||||
|
description = "We have PSI and can resolve this case correctly. I'm not sure if it should be fixed"
|
||||||
|
)
|
||||||
fun testBadlyNestedBlockInsideString() {
|
fun testBadlyNestedBlockInsideString() {
|
||||||
val before = "{\"{foo, ${c}bar\", baz}}"
|
val before = "{\"{foo, ${c}bar\", baz}}"
|
||||||
val keys = listOf("di{")
|
val keys = listOf("di{")
|
||||||
val after = "{\"{foo, ${c}bar\", baz}}"
|
val after = "{}}"
|
||||||
doTest(keys, before, after, Mode.NORMAL())
|
doTest(keys, before, after, Mode.NORMAL())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,6 +411,14 @@ class MotionActionTest : VimTestCase() {
|
|||||||
doTest(keys, before, after, Mode.INSERT)
|
doTest(keys, before, after, Mode.INSERT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeletingInnerBlockWhenItIsPresentInString() {
|
||||||
|
val before = "let variable = ('abc' .. \"br${c}aces ( with content )\")"
|
||||||
|
val keys = listOf("di(")
|
||||||
|
val after = "let variable = ()"
|
||||||
|
doTest(keys, before, after, Mode.NORMAL())
|
||||||
|
}
|
||||||
|
|
||||||
// VIM-1008 |c| |v_i{|
|
// VIM-1008 |c| |v_i{|
|
||||||
@Test
|
@Test
|
||||||
fun testDeleteInsideSingleQuotesSurroundedBlock() {
|
fun testDeleteInsideSingleQuotesSurroundedBlock() {
|
||||||
|
@ -42,10 +42,10 @@ class YankMotionActionTest : VimTestCase() {
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
configureByText(file)
|
configureByText(file)
|
||||||
|
|
||||||
val initialOffset = fixture.editor.caretModel.offset
|
val initialOffset = fixture.editor.caretModel
|
||||||
typeText("yy")
|
typeText("yy")
|
||||||
|
|
||||||
kotlin.test.assertEquals(initialOffset, fixture.editor.caretModel.offset)
|
kotlin.test.assertEquals(initialOffset, fixture.editor.caretModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DANGEROUS_CHARACTERS")
|
@Suppress("DANGEROUS_CHARACTERS")
|
||||||
|
@ -41,7 +41,7 @@ class SelectKeyHandlerTest : VimTestCase() {
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +169,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +283,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ Mode.INSERT,
|
|||||||
where it was settled on some sodden sand
|
where it was settled on some sodden sand
|
||||||
hard by the torrent of a mountain pass.
|
hard by the torrent of a mountain pass.
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
Mode.INSERT,
|
Mode.INSERT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,15 +98,6 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@VimBehaviorDiffers(
|
|
||||||
originalVimAfter = """
|
|
||||||
class Xxx $c{
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
@Test
|
@Test
|
||||||
fun `test go to next next bracket with great count`() {
|
fun `test go to next next bracket with great count`() {
|
||||||
doTest(
|
doTest(
|
||||||
@ -119,9 +110,9 @@ class MotionUnmatchedBraceOpenActionTest : VimTestCase() {
|
|||||||
}
|
}
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
"""
|
"""
|
||||||
class Xxx {
|
class Xxx $c{
|
||||||
int main() {
|
int main() {
|
||||||
$c
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
|
@ -8,10 +8,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.plugins.ideavim.action.motion.updown
|
package org.jetbrains.plugins.ideavim.action.motion.updown
|
||||||
|
|
||||||
|
import com.intellij.idea.TestFor
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
|
import org.junit.jupiter.api.Disabled
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,6 +145,7 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Disabled("It will work after implementing all of the methods in VimPsiService")
|
||||||
fun `test motion outside text`() {
|
fun `test motion outside text`() {
|
||||||
doTest(
|
doTest(
|
||||||
"%",
|
"%",
|
||||||
@ -207,41 +210,45 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
|
||||||
fun `test motion in text with escape (outer forward)`() {
|
fun `test motion in text with escape (outer forward)`() {
|
||||||
doTest(
|
doTest(
|
||||||
"%",
|
"%",
|
||||||
""" debugPrint$c(\(var)) """,
|
""" debugPrint$c(\(var)) """,
|
||||||
""" debugPrint(\(var)$c) """,
|
""" debugPrint(\(var$c)) """,
|
||||||
Mode.NORMAL(),
|
Mode.NORMAL(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
|
||||||
fun `test motion in text with escape (outer backward)`() {
|
fun `test motion in text with escape (outer backward)`() {
|
||||||
doTest(
|
doTest(
|
||||||
"%",
|
"%",
|
||||||
""" debugPrint(\(var)$c) """,
|
""" debugPrint(\(var)$c) """,
|
||||||
""" debugPrint$c(\(var)) """,
|
""" debugPrint(\(var)$c) """,
|
||||||
Mode.NORMAL(),
|
Mode.NORMAL(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
|
||||||
fun `test motion in text with escape (inner forward)`() {
|
fun `test motion in text with escape (inner forward)`() {
|
||||||
doTest(
|
doTest(
|
||||||
"%",
|
"%",
|
||||||
""" debugPrint(\$c(var)) """,
|
""" debugPrint(\$c(var)) """,
|
||||||
""" debugPrint(\(var$c)) """,
|
""" debugPrint(\$c(var)) """,
|
||||||
Mode.NORMAL(),
|
Mode.NORMAL(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.BUG_IN_NEOVIM)
|
||||||
fun `test motion in text with escape (inner backward)`() {
|
fun `test motion in text with escape (inner backward)`() {
|
||||||
doTest(
|
doTest(
|
||||||
"%",
|
"%",
|
||||||
""" debugPrint(\$c(var)) """,
|
""" debugPrint(\$c(var)) """,
|
||||||
""" debugPrint(\(var$c)) """,
|
""" debugPrint(\$c(var)) """,
|
||||||
Mode.NORMAL(),
|
Mode.NORMAL(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -332,4 +339,28 @@ class MotionPercentOrMatchActionTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
assertOffset(10)
|
assertOffset(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestFor(issues = ["VIM-3294"])
|
||||||
|
fun `test matching with braces inside of string`() {
|
||||||
|
configureByText("""
|
||||||
|
$c("("")")
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
("("")"$c)
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestFor(issues = ["VIM-3294"])
|
||||||
|
fun `test matching with braces inside of string 2`() {
|
||||||
|
configureByText("""
|
||||||
|
("("")"$c)
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
$c("("")")
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ package org.jetbrains.plugins.ideavim.common.editor
|
|||||||
|
|
||||||
import com.intellij.openapi.application.runWriteAction
|
import com.intellij.openapi.application.runWriteAction
|
||||||
import com.intellij.openapi.command.WriteCommandAction
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
import com.maddyhome.idea.vim.common.offset
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
@ -25,7 +24,7 @@ class VimEditorTest : VimTestCase() {
|
|||||||
val vimEditor = IjVimEditor(fixture.editor)
|
val vimEditor = IjVimEditor(fixture.editor)
|
||||||
WriteCommandAction.runWriteCommandAction(fixture.project) {
|
WriteCommandAction.runWriteCommandAction(fixture.project) {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
vimEditor.deleteRange(0.offset, 5.offset)
|
vimEditor.deleteRange(0, 5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertState("567890")
|
assertState("567890")
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.plugins.ideavim.extension.entiretextobj
|
package org.jetbrains.plugins.ideavim.extension.entiretextobj
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
@ -57,6 +58,14 @@ class VimTextObjEntireExtensionTest : VimTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testYankEntireBuffer() {
|
fun testYankEntireBuffer() {
|
||||||
doTest("yae", poem, "<caret>$poemNoCaret")
|
doTest("yae", poem, "<caret>$poemNoCaret")
|
||||||
|
assertRegisterString(injector.registerGroup.defaultRegister, poemNoCaret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// |y| |ae|
|
||||||
|
@Test
|
||||||
|
fun testYankEntireBufferWithCustomRegister() {
|
||||||
|
doTest("\"kyae", poem, "<caret>$poemNoCaret")
|
||||||
|
assertRegisterString('k', poemNoCaret)
|
||||||
}
|
}
|
||||||
|
|
||||||
// |gU| |ie|
|
// |gU| |ie|
|
||||||
|
@ -203,6 +203,36 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
|||||||
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `with specific register`() {
|
||||||
|
val text = "one ${c}two three four"
|
||||||
|
|
||||||
|
configureByText(text)
|
||||||
|
VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
|
||||||
|
typeText(injector.parser.parseKeys("\"kgriw"))
|
||||||
|
assertState("one on${c}e three four")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `with specific register in visual mode`() {
|
||||||
|
val text = "one ${c}two three four"
|
||||||
|
|
||||||
|
configureByText(text)
|
||||||
|
VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
|
||||||
|
typeText(injector.parser.parseKeys("ve\"kgr"))
|
||||||
|
assertState("one on${c}e three four")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `with specific register in line mode`() {
|
||||||
|
val text = "one ${c}two three four"
|
||||||
|
|
||||||
|
configureByText(text)
|
||||||
|
VimPlugin.getRegister().setKeys('k', injector.parser.parseKeys("one"))
|
||||||
|
typeText(injector.parser.parseKeys("\"kgrr"))
|
||||||
|
assertState("${c}one\n")
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------- grr --------------------------
|
// --------------------------------------- grr --------------------------
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -322,7 +322,7 @@ class CaretVisualAttributesHelperTest : VimTestCase() {
|
|||||||
)
|
)
|
||||||
injector.actionExecutor.executeAction(
|
injector.actionExecutor.executeAction(
|
||||||
"EditorCloneCaretBelow",
|
"EditorCloneCaretBelow",
|
||||||
injector.executionContextManager.onEditor(fixture.editor.vim),
|
injector.executionContextManager.getEditorExecutionContext(fixture.editor.vim),
|
||||||
)
|
)
|
||||||
kotlin.test.assertEquals(2, fixture.editor.caretModel.caretCount)
|
kotlin.test.assertEquals(2, fixture.editor.caretModel.caretCount)
|
||||||
assertCaretVisualAttributes(CaretVisualAttributes.Shape.BLOCK, 0f)
|
assertCaretVisualAttributes(CaretVisualAttributes.Shape.BLOCK, 0f)
|
||||||
|
@ -9,6 +9,7 @@ package org.jetbrains.plugins.ideavim.helper
|
|||||||
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
import com.maddyhome.idea.vim.helper.checkInString
|
import com.maddyhome.idea.vim.helper.checkInString
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
@ -246,8 +247,7 @@ class SearchHelperTest : VimTestCase() {
|
|||||||
fun findBlockRange(testCase: FindBlockRangeTestCase) {
|
fun findBlockRange(testCase: FindBlockRangeTestCase) {
|
||||||
val (_, text, type, count, isOuter, expected) = (testCase)
|
val (_, text, type, count, isOuter, expected) = (testCase)
|
||||||
configureByText(text)
|
configureByText(text)
|
||||||
val actual =
|
val actual = findBlockRange(fixture.editor.vim, fixture.editor.vim.currentCaret(), type, count, isOuter)
|
||||||
injector.searchHelper.findBlockRange(fixture.editor.vim, fixture.editor.vim.currentCaret(), type, count, isOuter)
|
|
||||||
kotlin.test.assertEquals(expected, actual)
|
kotlin.test.assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,8 +294,8 @@ class SearchHelperTest : VimTestCase() {
|
|||||||
FindBlockRangeTestCase("outer match exclude start paren in string when caret at start of quote", "(${c}\"(aa\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
FindBlockRangeTestCase("outer match exclude start paren in string when caret at start of quote", "(${c}\"(aa\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
||||||
FindBlockRangeTestCase("inner match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
FindBlockRangeTestCase("inner match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
||||||
FindBlockRangeTestCase("outer match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
FindBlockRangeTestCase("outer match exclude start paren in string when caret at end of quote", "(\"(aa${c}\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
||||||
FindBlockRangeTestCase("inner match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = false, expected = null),
|
FindBlockRangeTestCase("inner match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = false, expected = TextRange(1, 6)), // Vim behavior differs, but we have some PSI magic and can resolve such cases
|
||||||
FindBlockRangeTestCase("outer match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = true, expected = null),
|
FindBlockRangeTestCase("outer match not exclude start paren in string when caret in between quote", "(\"(a${c}a\")", '(', 1, isOuter = true, expected = TextRange(0, 7)), // Vim behavior differs, but we have some PSI magic and can resolve such cases
|
||||||
FindBlockRangeTestCase("inner match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
FindBlockRangeTestCase("inner match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
||||||
FindBlockRangeTestCase("outer match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
FindBlockRangeTestCase("outer match exclude end paren in string when caret at start of quote", "(${c}\"aa)\")", '(', 1, isOuter = true, expected = TextRange(0, 7)),
|
||||||
FindBlockRangeTestCase("inner match exclude end paren in string when caret at end of quote", "(\"aa)${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
FindBlockRangeTestCase("inner match exclude end paren in string when caret at end of quote", "(\"aa)${c}\")", '(', 1, isOuter = false, expected = TextRange(1, 6)),
|
||||||
|
@ -79,7 +79,7 @@ class VimRegexEngineTest : VimTestCase() {
|
|||||||
configureByText("Lor${c}em ${c}Ipsum")
|
configureByText("Lor${c}em ${c}Ipsum")
|
||||||
val editor = fixture.editor.vim
|
val editor = fixture.editor.vim
|
||||||
val mark = VimMark.create('m', 0, 0, editor.getPath(), editor.extractProtocol())!!
|
val mark = VimMark.create('m', 0, 0, editor.getPath(), editor.extractProtocol())!!
|
||||||
val secondCaret = editor.carets().maxByOrNull { it.offset.point }!!
|
val secondCaret = editor.carets().maxByOrNull { it.offset }!!
|
||||||
secondCaret.markStorage.setMark(mark)
|
secondCaret.markStorage.setMark(mark)
|
||||||
|
|
||||||
val result = findAll("\\%>'m\\%#.")
|
val result = findAll("\\%>'m\\%#.")
|
||||||
|
@ -245,6 +245,9 @@ enum class SkipNeovimReason {
|
|||||||
|
|
||||||
GUARDED_BLOCKS,
|
GUARDED_BLOCKS,
|
||||||
CTRL_CODES,
|
CTRL_CODES,
|
||||||
|
|
||||||
|
BUG_IN_NEOVIM,
|
||||||
|
PSI,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LogicalPosition.toVimCoords(): VimCoords {
|
fun LogicalPosition.toVimCoords(): VimCoords {
|
||||||
|
@ -15,7 +15,9 @@ import com.intellij.openapi.actionSystem.ActionManager
|
|||||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||||
|
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||||
import com.intellij.openapi.application.PathManager
|
import com.intellij.openapi.application.PathManager
|
||||||
import com.intellij.openapi.application.WriteAction
|
import com.intellij.openapi.application.WriteAction
|
||||||
import com.intellij.openapi.editor.CaretVisualAttributes
|
import com.intellij.openapi.editor.CaretVisualAttributes
|
||||||
@ -25,6 +27,7 @@ import com.intellij.openapi.editor.LogicalPosition
|
|||||||
import com.intellij.openapi.editor.VisualPosition
|
import com.intellij.openapi.editor.VisualPosition
|
||||||
import com.intellij.openapi.editor.colors.EditorColors
|
import com.intellij.openapi.editor.colors.EditorColors
|
||||||
import com.intellij.openapi.editor.ex.EditorEx
|
import com.intellij.openapi.editor.ex.EditorEx
|
||||||
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
import com.intellij.openapi.fileTypes.FileType
|
import com.intellij.openapi.fileTypes.FileType
|
||||||
import com.intellij.openapi.fileTypes.PlainTextFileType
|
import com.intellij.openapi.fileTypes.PlainTextFileType
|
||||||
@ -64,7 +67,6 @@ import com.maddyhome.idea.vim.key.ToKeysMappingInfo
|
|||||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
|
||||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.options.OptionAccessScope
|
import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||||
@ -452,6 +454,11 @@ abstract class VimTestCase {
|
|||||||
assertEquals(expected, actual, "Wrong register contents")
|
assertEquals(expected, actual, "Wrong register contents")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun assertRegisterString(char: Char, expected: String?) {
|
||||||
|
val actual = injector.registerGroup.getRegister(char)?.keys?.let(injector.parser::toPrintableString)
|
||||||
|
assertEquals(expected, actual, "Wrong register contents")
|
||||||
|
}
|
||||||
|
|
||||||
protected fun assertState(modeAfter: Mode) {
|
protected fun assertState(modeAfter: Mode) {
|
||||||
assertMode(modeAfter)
|
assertMode(modeAfter)
|
||||||
assertCaretsVisualAttributes()
|
assertCaretsVisualAttributes()
|
||||||
@ -755,9 +762,13 @@ abstract class VimTestCase {
|
|||||||
val event =
|
val event =
|
||||||
KeyEvent(editor.component, KeyEvent.KEY_PRESSED, Date().time, key.modifiers, key.keyCode, key.keyChar)
|
KeyEvent(editor.component, KeyEvent.KEY_PRESSED, Date().time, key.modifiers, key.keyCode, key.keyChar)
|
||||||
|
|
||||||
|
val context = SimpleDataContext.builder()
|
||||||
|
.setParent(EditorUtil.getEditorDataContext(editor))
|
||||||
|
.add(PlatformCoreDataKeys.CONTEXT_COMPONENT, editor.component)
|
||||||
|
.build()
|
||||||
val e = AnActionEvent(
|
val e = AnActionEvent(
|
||||||
event,
|
event,
|
||||||
injector.executionContextManager.onEditor(editor.vim).ij,
|
context,
|
||||||
ActionPlaces.KEYBOARD_SHORTCUT,
|
ActionPlaces.KEYBOARD_SHORTCUT,
|
||||||
VimShortcutKeyAction.instance.templatePresentation.clone(),
|
VimShortcutKeyAction.instance.templatePresentation.clone(),
|
||||||
ActionManager.getInstance(),
|
ActionManager.getInstance(),
|
||||||
@ -810,7 +821,7 @@ abstract class VimTestCase {
|
|||||||
|
|
||||||
fun typeText(keys: List<KeyStroke?>, editor: Editor, project: Project?) {
|
fun typeText(keys: List<KeyStroke?>, editor: Editor, project: Project?) {
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
val dataContext = injector.executionContextManager.onEditor(editor.vim)
|
val dataContext = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||||
TestInputModel.getInstance(editor).setKeyStrokes(keys.filterNotNull())
|
TestInputModel.getInstance(editor).setKeyStrokes(keys.filterNotNull())
|
||||||
runWriteCommand(
|
runWriteCommand(
|
||||||
project,
|
project,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.plugins.ideavim.action.motion.updown
|
package org.jetbrains.plugins.ideavim.action.motion.updown
|
||||||
|
|
||||||
|
import com.intellij.idea.TestFor
|
||||||
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
import org.jetbrains.plugins.ideavim.SkipNeovimReason
|
||||||
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
import org.jetbrains.plugins.ideavim.TestWithoutNeovim
|
||||||
import org.jetbrains.plugins.ideavim.VimJavaTestCase
|
import org.jetbrains.plugins.ideavim.VimJavaTestCase
|
||||||
@ -66,4 +67,98 @@ class MotionPercentOrMatchActionJavaTest : VimJavaTestCase() {
|
|||||||
typeText("%")
|
typeText("%")
|
||||||
assertState("/* foo $c */")
|
assertState("/* foo $c */")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestFor(issues = ["VIM-1399"])
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.PSI)
|
||||||
|
fun `test percent ignores brace inside comment`() {
|
||||||
|
configureByJavaText("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
TokenStream result = new EmptyTokenFilter(in); /* $c{
|
||||||
|
* some text
|
||||||
|
*/
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
TokenStream result = new EmptyTokenFilter(in); /* $c{
|
||||||
|
* some text
|
||||||
|
*/
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestFor(issues = ["VIM-1399"])
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.PSI)
|
||||||
|
fun `test percent doesnt match brace inside comment`() {
|
||||||
|
configureByJavaText("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) $c{
|
||||||
|
TokenStream result = new EmptyTokenFilter(in); /* {
|
||||||
|
* some text
|
||||||
|
*/
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
TokenStream result = new EmptyTokenFilter(in); /* {
|
||||||
|
* some text
|
||||||
|
*/
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
return result;
|
||||||
|
$c}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.PSI)
|
||||||
|
fun `test matching works with a sequence of single-line comments`() {
|
||||||
|
configureByJavaText("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
// $c{
|
||||||
|
// result = new LowerCaseFilter(result);
|
||||||
|
// }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
// {
|
||||||
|
// result = new LowerCaseFilter(result);
|
||||||
|
// $c}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestWithoutNeovim(SkipNeovimReason.PSI)
|
||||||
|
fun `test matching doesn't work if a sequence of single-line comments is broken`() {
|
||||||
|
configureByJavaText("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
// $c{
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
// }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
typeText("%")
|
||||||
|
assertState("""
|
||||||
|
protected TokenStream normalize(String fieldName, TokenStream in) {
|
||||||
|
// $c{
|
||||||
|
result = new LowerCaseFilter(result);
|
||||||
|
// }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
}
|
}
|
@ -10,7 +10,9 @@ package ui
|
|||||||
|
|
||||||
import com.automation.remarks.junit5.Video
|
import com.automation.remarks.junit5.Video
|
||||||
import com.intellij.remoterobot.RemoteRobot
|
import com.intellij.remoterobot.RemoteRobot
|
||||||
|
import com.intellij.remoterobot.fixtures.ComponentFixture
|
||||||
import com.intellij.remoterobot.fixtures.ContainerFixture
|
import com.intellij.remoterobot.fixtures.ContainerFixture
|
||||||
|
import com.intellij.remoterobot.search.locators.byXpath
|
||||||
import com.intellij.remoterobot.steps.CommonSteps
|
import com.intellij.remoterobot.steps.CommonSteps
|
||||||
import com.intellij.remoterobot.stepsProcessing.step
|
import com.intellij.remoterobot.stepsProcessing.step
|
||||||
import com.intellij.remoterobot.utils.keyboard
|
import com.intellij.remoterobot.utils.keyboard
|
||||||
@ -120,6 +122,9 @@ class UiTests {
|
|||||||
Thread.sleep(5000)
|
Thread.sleep(5000)
|
||||||
wrapWithIf(javaEditor)
|
wrapWithIf(javaEditor)
|
||||||
testTrackActionId(javaEditor)
|
testTrackActionId(javaEditor)
|
||||||
|
testActionGenerate(javaEditor)
|
||||||
|
testActionNewElementSamePlace(javaEditor)
|
||||||
|
testActionCopy(javaEditor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,6 +243,66 @@ class UiTests {
|
|||||||
vimExit()
|
vimExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IdeaFrame.testActionGenerate(editor: Editor) {
|
||||||
|
val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
assertTrue(label.isEmpty())
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
enterText(":action Generate")
|
||||||
|
enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitFor {
|
||||||
|
val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
if (generateDialog.size == 1) {
|
||||||
|
return@waitFor generateDialog.single().hasText("Generate")
|
||||||
|
}
|
||||||
|
return@waitFor false
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard { escape() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IdeaFrame.testActionNewElementSamePlace(editor: Editor) {
|
||||||
|
val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
assertTrue(label.isEmpty())
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
enterText(":action NewElementSamePlace")
|
||||||
|
enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitFor {
|
||||||
|
val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
if (generateDialog.size == 1) {
|
||||||
|
return@waitFor generateDialog.single().hasText("New in This Directory")
|
||||||
|
}
|
||||||
|
return@waitFor false
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard { escape() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IdeaFrame.testActionCopy(editor: Editor) {
|
||||||
|
val label = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
assertTrue(label.isEmpty())
|
||||||
|
|
||||||
|
keyboard {
|
||||||
|
enterText(":action CopyReferencePopupGroup")
|
||||||
|
enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitFor {
|
||||||
|
val generateDialog = findAll<ComponentFixture>(byXpath("//div[@class='EngravedLabel']"))
|
||||||
|
if (generateDialog.size == 1) {
|
||||||
|
return@waitFor generateDialog.single().hasText("Copy Path/Reference…")
|
||||||
|
}
|
||||||
|
return@waitFor false
|
||||||
|
}
|
||||||
|
|
||||||
|
keyboard { escape() }
|
||||||
|
}
|
||||||
|
|
||||||
private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) {
|
private fun IdeaFrame.createFile(fileName: String, remoteRobot: RemoteRobot) {
|
||||||
step("Create $fileName file") {
|
step("Create $fileName file") {
|
||||||
with(projectViewTree) {
|
with(projectViewTree) {
|
||||||
|
@ -48,7 +48,11 @@ class PyCharmTest {
|
|||||||
findAllText("Python Packages").isNotEmpty() &&
|
findAllText("Python Packages").isNotEmpty() &&
|
||||||
isSmartMode()
|
isSmartMode()
|
||||||
}
|
}
|
||||||
findText("Python Console").click()
|
|
||||||
|
// Open tool window by id.
|
||||||
|
// id taken from PythonConsoleToolWindowFactory.ID but it's not resolved in robot by some reason
|
||||||
|
// the last 'x' is just to return some serializable value
|
||||||
|
callJs<String>("com.intellij.openapi.wm.ToolWindowManager.getInstance(component.project).getToolWindow('Python Console').activate(null, true); 'x'", true)
|
||||||
|
|
||||||
Thread.sleep(10_000)
|
Thread.sleep(10_000)
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ dependencies {
|
|||||||
compileOnly(project(":annotation-processors"))
|
compileOnly(project(":annotation-processors"))
|
||||||
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion")
|
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion")
|
||||||
|
|
||||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
|
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
|
@ -59,7 +59,7 @@ private fun changeCharacter(editor: VimEditor, caret: VimCaret, count: Int, ch:
|
|||||||
val col = caret.getBufferPosition().column
|
val col = caret.getBufferPosition().column
|
||||||
// TODO: Is this correct? Should we really use only current caret? We have a caret as an argument
|
// TODO: Is this correct? Should we really use only current caret? We have a caret as an argument
|
||||||
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
||||||
val offset = caret.offset.point
|
val offset = caret.offset
|
||||||
if (len - col < count) {
|
if (len - col < count) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ public class ChangeLastGlobalSearchReplaceAction : ChangeEditorActionHandler.Sin
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val range = LineRange(0, editor.lineCount() - 1)
|
val range = LineRange(0, editor.lineCount() - 1)
|
||||||
return injector.searchGroup
|
return injector.searchGroup
|
||||||
.processSubstituteCommand(editor, editor.primaryCaret(), range, "s", "//~/&", Script(listOf()))
|
.processSubstituteCommand(editor, editor.primaryCaret(), context, range, "s", "//~/&", Script(listOf()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public class ChangeLastSearchReplaceAction : ChangeEditorActionHandler.SingleExe
|
|||||||
for (caret in editor.carets()) {
|
for (caret in editor.carets()) {
|
||||||
val line = caret.getBufferPosition().line
|
val line = caret.getBufferPosition().line
|
||||||
if (!injector.searchGroup
|
if (!injector.searchGroup
|
||||||
.processSubstituteCommand(editor, caret, LineRange(line, line), "s", "//~/", Script(listOf()))
|
.processSubstituteCommand(editor, caret, context, LineRange(line, line), "s", "//~/", Script(listOf()))
|
||||||
) {
|
) {
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ private fun insertCharacterAroundCursor(editor: VimEditor, caret: VimCaret, dir:
|
|||||||
vp = VimVisualPosition(vp.line + dir, vp.column, false)
|
vp = VimVisualPosition(vp.line + dir, vp.column, false)
|
||||||
val len = editor.lineLength(editor.visualLineToBufferLine(vp.line))
|
val len = editor.lineLength(editor.visualLineToBufferLine(vp.line))
|
||||||
if (vp.column < len) {
|
if (vp.column < len) {
|
||||||
val offset = editor.visualPositionToOffset(VimVisualPosition(vp.line, vp.column, false)).point
|
val offset = editor.visualPositionToOffset(VimVisualPosition(vp.line, vp.column, false))
|
||||||
val charsSequence = editor.text()
|
val charsSequence = editor.text()
|
||||||
if (offset < charsSequence.length) {
|
if (offset < charsSequence.length) {
|
||||||
val ch = charsSequence[offset]
|
val ch = charsSequence[offset]
|
||||||
|
@ -17,11 +17,10 @@ import com.maddyhome.idea.vim.command.Argument
|
|||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
|
||||||
import com.maddyhome.idea.vim.common.Offset
|
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<C-U>"], modes = [Mode.INSERT])
|
@CommandOrMotion(keys = ["<C-U>"], modes = [Mode.INSERT])
|
||||||
@ -57,13 +56,13 @@ private fun insertDeleteInsertedText(
|
|||||||
var deleteTo = caret.vimInsertStart.startOffset
|
var deleteTo = caret.vimInsertStart.startOffset
|
||||||
val offset = caret.offset
|
val offset = caret.offset
|
||||||
if (offset == deleteTo) {
|
if (offset == deleteTo) {
|
||||||
deleteTo = Offset(injector.motion.moveCaretToCurrentLineStartSkipLeading(editor, caret))
|
deleteTo = injector.motion.moveCaretToCurrentLineStartSkipLeading(editor, caret)
|
||||||
}
|
}
|
||||||
if (deleteTo.point != -1) {
|
if (deleteTo != -1) {
|
||||||
injector.changeGroup.deleteRange(
|
injector.changeGroup.deleteRange(
|
||||||
editor,
|
editor,
|
||||||
caret,
|
caret,
|
||||||
TextRange(deleteTo.point, offset.point),
|
TextRange(deleteTo, offset),
|
||||||
SelectionType.CHARACTER_WISE,
|
SelectionType.CHARACTER_WISE,
|
||||||
false,
|
false,
|
||||||
operatorArguments,
|
operatorArguments,
|
||||||
|
@ -17,11 +17,11 @@ import com.maddyhome.idea.vim.command.Argument
|
|||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<C-W>"], modes = [Mode.INSERT])
|
@CommandOrMotion(keys = ["<C-W>"], modes = [Mode.INSERT])
|
||||||
@ -52,9 +52,9 @@ public class InsertDeletePreviousWordAction : ChangeEditorActionHandler.ForEachC
|
|||||||
*/
|
*/
|
||||||
private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operatorArguments: OperatorArguments): Boolean {
|
private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operatorArguments: OperatorArguments): Boolean {
|
||||||
val deleteTo: Int = if (caret.getBufferPosition().column == 0) {
|
val deleteTo: Int = if (caret.getBufferPosition().column == 0) {
|
||||||
caret.offset.point - 1
|
caret.offset - 1
|
||||||
} else {
|
} else {
|
||||||
var pointer = caret.offset.point - 1
|
var pointer = caret.offset - 1
|
||||||
val chars = editor.text()
|
val chars = editor.text()
|
||||||
while (pointer >= 0 && chars[pointer] == ' ' && chars[pointer] != '\n') {
|
while (pointer >= 0 && chars[pointer] == ' ' && chars[pointer] != '\n') {
|
||||||
pointer--
|
pointer--
|
||||||
@ -73,7 +73,7 @@ private fun insertDeletePreviousWord(editor: VimEditor, caret: VimCaret, operato
|
|||||||
if (deleteTo < 0) {
|
if (deleteTo < 0) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val range = TextRange(deleteTo, caret.offset.point)
|
val range = TextRange(deleteTo, caret.offset)
|
||||||
injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true, operatorArguments)
|
injector.changeGroup.deleteRange(editor, caret, range, SelectionType.CHARACTER_WISE, true, operatorArguments)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import com.maddyhome.idea.vim.api.injector
|
|||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.common.Offset
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
|
||||||
@ -79,11 +78,7 @@ private fun insertNewLineAbove(editor: VimEditor, context: ExecutionContext) {
|
|||||||
// Check if the "last character on previous line" has a guard
|
// Check if the "last character on previous line" has a guard
|
||||||
// This is actively used in pycharm notebooks https://youtrack.jetbrains.com/issue/VIM-2495
|
// This is actively used in pycharm notebooks https://youtrack.jetbrains.com/issue/VIM-2495
|
||||||
val hasGuards = moves.stream().anyMatch { (_, second): Pair<VimCaret?, Int?> ->
|
val hasGuards = moves.stream().anyMatch { (_, second): Pair<VimCaret?, Int?> ->
|
||||||
editor.document.getOffsetGuard(
|
editor.document.getOffsetGuard(second!!) != null
|
||||||
Offset(
|
|
||||||
second!!,
|
|
||||||
),
|
|
||||||
) != null
|
|
||||||
}
|
}
|
||||||
if (!hasGuards) {
|
if (!hasGuards) {
|
||||||
for ((first, second) in moves) {
|
for ((first, second) in moves) {
|
||||||
|
@ -15,12 +15,12 @@ import com.maddyhome.idea.vim.api.injector
|
|||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
|
||||||
import com.maddyhome.idea.vim.ex.ExException
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.RWLockLabel
|
import com.maddyhome.idea.vim.helper.RWLockLabel
|
||||||
import com.maddyhome.idea.vim.put.PutData
|
import com.maddyhome.idea.vim.put.PutData
|
||||||
import com.maddyhome.idea.vim.register.Register
|
import com.maddyhome.idea.vim.register.Register
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.vimscript.model.Script
|
import com.maddyhome.idea.vim.vimscript.model.Script
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT])
|
@CommandOrMotion(keys = ["<C-R>"], modes = [Mode.INSERT])
|
||||||
@ -40,7 +40,7 @@ public class InsertRegisterAction : VimActionHandler.SingleExecution() {
|
|||||||
if (argument?.character == '=') {
|
if (argument?.character == '=') {
|
||||||
injector.application.invokeLater {
|
injector.application.invokeLater {
|
||||||
try {
|
try {
|
||||||
val expression = readExpression(editor)
|
val expression = readExpression(editor, context)
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
if (expression.isNotEmpty()) {
|
if (expression.isNotEmpty()) {
|
||||||
val expressionValue =
|
val expressionValue =
|
||||||
@ -62,8 +62,8 @@ public class InsertRegisterAction : VimActionHandler.SingleExecution() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readExpression(editor: VimEditor): String? {
|
private fun readExpression(editor: VimEditor, context: ExecutionContext): String? {
|
||||||
return injector.commandLineHelper.inputString(editor, "=", null)
|
return injector.commandLineHelper.inputString(editor, context, "=", null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ import com.maddyhome.idea.vim.command.MotionType
|
|||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.Motion
|
import com.maddyhome.idea.vim.handler.Motion
|
||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||||
import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
|
|
||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
|
import com.maddyhome.idea.vim.state.mode.isInsertionAllowed
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["g$", "g<End>"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
|
@CommandOrMotion(keys = ["g$", "g<End>"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
|
||||||
public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
|
public class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
|
||||||
|
@ -34,7 +34,7 @@ public class MotionShiftLeftAction : ShiftedArrowKeyHandler(true) {
|
|||||||
|
|
||||||
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||||
val caret = editor.currentCaret()
|
val caret = editor.currentCaret()
|
||||||
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -cmd.count, false)
|
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset, -cmd.count, false)
|
||||||
caret.moveToMotion(newOffset)
|
caret.moveToMotion(newOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public class MotionShiftRightAction : ShiftedArrowKeyHandler(true) {
|
|||||||
|
|
||||||
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
override fun motionWithoutKeyModel(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||||
val caret = editor.currentCaret()
|
val caret = editor.currentCaret()
|
||||||
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset.point, cmd.count, false)
|
val newOffset = injector.motion.findOffsetOfNextWord(editor, caret.offset, cmd.count, false)
|
||||||
if (newOffset is Motion.AbsoluteOffset) {
|
if (newOffset is Motion.AbsoluteOffset) {
|
||||||
caret.moveToOffset(newOffset.offset)
|
caret.moveToOffset(newOffset.offset)
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@ import com.intellij.vim.annotations.Mode
|
|||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -35,7 +35,7 @@ public class MotionInnerBlockAngleAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '<', count, false)
|
return findBlockRange(editor, caret, '<', count, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ public class MotionInnerBlockBraceAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '{', count, false)
|
return findBlockRange(editor, caret, '{', count, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ public class MotionInnerBlockBracketAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '[', count, false)
|
return findBlockRange(editor, caret, '[', count, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ public class MotionInnerBlockParenAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '(', count, false)
|
return findBlockRange(editor, caret, '(', count, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ public class MotionOuterBlockAngleAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '<', count, true)
|
return findBlockRange(editor, caret, '<', count, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ public class MotionOuterBlockBraceAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '{', count, true)
|
return findBlockRange(editor, caret, '{', count, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ public class MotionOuterBlockBracketAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '[', count, true)
|
return findBlockRange(editor, caret, '[', count, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +161,6 @@ public class MotionOuterBlockParenAction : TextObjectActionHandler() {
|
|||||||
count: Int,
|
count: Int,
|
||||||
rawCount: Int,
|
rawCount: Int,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
return injector.searchHelper.findBlockRange(editor, caret, '(', count, true)
|
return findBlockRange(editor, caret, '(', count, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,10 @@ import com.maddyhome.idea.vim.api.ExecutionContext
|
|||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
|
||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
import com.maddyhome.idea.vim.state.mode.mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["iW"], modes = [com.intellij.vim.annotations.Mode.VISUAL, com.intellij.vim.annotations.Mode.OP_PENDING])
|
@CommandOrMotion(keys = ["iW"], modes = [com.intellij.vim.annotations.Mode.VISUAL, com.intellij.vim.annotations.Mode.OP_PENDING])
|
||||||
public class MotionInnerBigWordAction : TextObjectActionHandler() {
|
public class MotionInnerBigWordAction : TextObjectActionHandler() {
|
||||||
@ -93,10 +92,10 @@ private fun getWordRange(
|
|||||||
var dir = 1
|
var dir = 1
|
||||||
var selection = false
|
var selection = false
|
||||||
if (editor.mode is Mode.VISUAL) {
|
if (editor.mode is Mode.VISUAL) {
|
||||||
if (caret.vimSelectionStart > caret.offset.point) {
|
if (caret.vimSelectionStart > caret.offset) {
|
||||||
dir = -1
|
dir = -1
|
||||||
}
|
}
|
||||||
if (caret.vimSelectionStart != caret.offset.point) {
|
if (caret.vimSelectionStart != caret.offset) {
|
||||||
selection = true
|
selection = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class SearchEntryFwdAction : MotionActionHandler.ForEachCaret() {
|
|||||||
): Motion {
|
): Motion {
|
||||||
if (argument == null) return Motion.Error
|
if (argument == null) return Motion.Error
|
||||||
return injector.searchGroup
|
return injector.searchGroup
|
||||||
.processSearchCommand(editor, argument.string, caret.offset.point, Direction.FORWARDS).toMotionOrError()
|
.processSearchCommand(editor, argument.string, caret.offset, Direction.FORWARDS).toMotionOrError()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
@ -39,7 +39,7 @@ public class SearchEntryRevAction : MotionActionHandler.ForEachCaret() {
|
|||||||
): Motion {
|
): Motion {
|
||||||
if (argument == null) return Motion.Error
|
if (argument == null) return Motion.Error
|
||||||
return injector.searchGroup
|
return injector.searchGroup
|
||||||
.processSearchCommand(editor, argument.string, caret.offset.point, Direction.BACKWARDS).toMotionOrError()
|
.processSearchCommand(editor, argument.string, caret.offset, Direction.BACKWARDS).toMotionOrError()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
@ -16,9 +16,9 @@ import com.maddyhome.idea.vim.api.getLineEndForOffset
|
|||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
|
||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
@ -36,10 +36,10 @@ public class SelectEnableBlockModeAction : VimActionHandler.SingleExecution() {
|
|||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
editor.removeSecondaryCarets()
|
editor.removeSecondaryCarets()
|
||||||
val lineEnd = editor.getLineEndForOffset(editor.primaryCaret().offset.point)
|
val lineEnd = editor.getLineEndForOffset(editor.primaryCaret().offset)
|
||||||
editor.primaryCaret().run {
|
editor.primaryCaret().run {
|
||||||
vimSetSystemSelectionSilently(offset.point, (offset.point + 1).coerceAtMost(lineEnd))
|
vimSetSystemSelectionSilently(offset, (offset + 1).coerceAtMost(lineEnd))
|
||||||
moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
|
moveToInlayAwareOffset((offset + 1).coerceAtMost(lineEnd))
|
||||||
}
|
}
|
||||||
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
|
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.BLOCK_WISE)
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@ import com.maddyhome.idea.vim.api.getLineEndForOffset
|
|||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
|
||||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
@ -35,11 +35,11 @@ public class SelectEnableCharacterModeAction : VimActionHandler.SingleExecution(
|
|||||||
cmd: Command,
|
cmd: Command,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||||
val lineEnd = editor.getLineEndForOffset(caret.offset.point)
|
val lineEnd = editor.getLineEndForOffset(caret.offset)
|
||||||
caret.run {
|
caret.run {
|
||||||
vimSetSystemSelectionSilently(offset.point, (offset.point + 1).coerceAtMost(lineEnd))
|
vimSetSystemSelectionSilently(offset, (offset + 1).coerceAtMost(lineEnd))
|
||||||
moveToInlayAwareOffset((offset.point + 1).coerceAtMost(lineEnd))
|
moveToInlayAwareOffset((offset + 1).coerceAtMost(lineEnd))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
|
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.CHARACTER_WISE)
|
||||||
|
@ -37,8 +37,8 @@ public class SelectEnableLineModeAction : VimActionHandler.SingleExecution() {
|
|||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
editor.nativeCarets().forEach { caret ->
|
editor.nativeCarets().forEach { caret ->
|
||||||
val lineEnd = editor.getLineEndForOffset(caret.offset.point)
|
val lineEnd = editor.getLineEndForOffset(caret.offset)
|
||||||
val lineStart = editor.getLineStartForOffset(caret.offset.point)
|
val lineStart = editor.getLineStartForOffset(caret.offset)
|
||||||
caret.vimSetSystemSelectionSilently(lineStart, lineEnd)
|
caret.vimSetSystemSelectionSilently(lineStart, lineEnd)
|
||||||
}
|
}
|
||||||
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
|
return injector.visualMotionGroup.enterSelectMode(editor, SelectionType.LINE_WISE)
|
||||||
|
@ -48,8 +48,8 @@ public class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
|
|||||||
editor.setSelectMode(myMode.selectionType)
|
editor.setSelectMode(myMode.selectionType)
|
||||||
if (myMode.selectionType != SelectionType.LINE_WISE) {
|
if (myMode.selectionType != SelectionType.LINE_WISE) {
|
||||||
editor.nativeCarets().forEach {
|
editor.nativeCarets().forEach {
|
||||||
if (it.offset.point + injector.visualMotionGroup.selectionAdj == it.selectionEnd) {
|
if (it.offset + injector.visualMotionGroup.selectionAdj == it.selectionEnd) {
|
||||||
it.moveToInlayAwareOffset(it.offset.point + injector.visualMotionGroup.selectionAdj)
|
it.moveToInlayAwareOffset(it.offset + injector.visualMotionGroup.selectionAdj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,8 +57,8 @@ public class SelectToggleVisualMode : VimActionHandler.SingleExecution() {
|
|||||||
editor.pushVisualMode(myMode.selectionType)
|
editor.pushVisualMode(myMode.selectionType)
|
||||||
if (myMode.selectionType != SelectionType.LINE_WISE) {
|
if (myMode.selectionType != SelectionType.LINE_WISE) {
|
||||||
editor.nativeCarets().forEach {
|
editor.nativeCarets().forEach {
|
||||||
if (it.offset.point == it.selectionEnd && it.visualLineStart <= it.offset.point - injector.visualMotionGroup.selectionAdj) {
|
if (it.offset == it.selectionEnd && it.visualLineStart <= it.offset - injector.visualMotionGroup.selectionAdj) {
|
||||||
it.moveToInlayAwareOffset(it.offset.point - injector.visualMotionGroup.selectionAdj)
|
it.moveToInlayAwareOffset(it.offset - injector.visualMotionGroup.selectionAdj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class SelectMotionLeftAction : MotionActionHandler.ForEachCaret() {
|
|||||||
if (editor.isTemplateActive()) {
|
if (editor.isTemplateActive()) {
|
||||||
logger.debug("Template is active. Activate insert mode")
|
logger.debug("Template is active. Activate insert mode")
|
||||||
injector.changeGroup.insertBeforeCursor(editor, context)
|
injector.changeGroup.insertBeforeCursor(editor, context)
|
||||||
if (caret.offset.point in startSelection..endSelection) {
|
if (caret.offset in startSelection..endSelection) {
|
||||||
return startSelection.toMotion()
|
return startSelection.toMotion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,11 +48,11 @@ public class SelectMotionRightAction : MotionActionHandler.ForEachCaret() {
|
|||||||
if (editor.isTemplateActive()) {
|
if (editor.isTemplateActive()) {
|
||||||
logger.debug("Template is active. Activate insert mode")
|
logger.debug("Template is active. Activate insert mode")
|
||||||
injector.changeGroup.insertBeforeCursor(editor, context)
|
injector.changeGroup.insertBeforeCursor(editor, context)
|
||||||
if (caret.offset.point in startSelection..endSelection) {
|
if (caret.offset in startSelection..endSelection) {
|
||||||
return endSelection.toMotion()
|
return endSelection.toMotion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return caret.offset.point.toMotion()
|
return caret.offset.toMotion()
|
||||||
}
|
}
|
||||||
return injector.motion.getHorizontalMotion(editor, caret, operatorArguments.count1, false)
|
return injector.motion.getHorizontalMotion(editor, caret, operatorArguments.count1, false)
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,14 @@ public sealed class WordEndAction(public val direction: Direction, public val bi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun moveCaretToNextWordEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bigWord: Boolean): Motion {
|
private fun moveCaretToNextWordEnd(editor: VimEditor, caret: ImmutableVimCaret, count: Int, bigWord: Boolean): Motion {
|
||||||
if (caret.offset.point == 0 && count < 0 || caret.offset.point >= editor.fileSize() - 1 && count > 0) {
|
if (caret.offset == 0 && count < 0 || caret.offset >= editor.fileSize() - 1 && count > 0) {
|
||||||
return Motion.Error
|
return Motion.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are doing this move as part of a change command (e.q. cw), we need to count the current end of
|
// If we are doing this move as part of a change command (e.q. cw), we need to count the current end of
|
||||||
// word if the cursor happens to be on the end of a word already. If this is a normal move, we don't count
|
// word if the cursor happens to be on the end of a word already. If this is a normal move, we don't count
|
||||||
// the current word.
|
// the current word.
|
||||||
val pos = injector.searchHelper.findNextWordEnd(editor, caret.offset.point, count, bigWord, false)
|
val pos = injector.searchHelper.findNextWordEnd(editor, caret.offset, count, bigWord, false)
|
||||||
return if (pos == -1) {
|
return if (pos == -1) {
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
AbsoluteOffset(injector.motion.moveCaretToLineStart(editor, 0))
|
AbsoluteOffset(injector.motion.moveCaretToLineStart(editor, 0))
|
||||||
|
@ -28,7 +28,7 @@ public class MotionBigWordLeftAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.motion.findOffsetOfNextWord(editor, caret.offset.point, -operatorArguments.count1, true)
|
return injector.motion.findOffsetOfNextWord(editor, caret.offset, -operatorArguments.count1, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
@ -28,7 +28,7 @@ public class MotionBigWordRightAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.motion.findOffsetOfNextWord(editor, caret.offset.point, operatorArguments.count1, true)
|
return injector.motion.findOffsetOfNextWord(editor, caret.offset, operatorArguments.count1, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
@ -31,7 +31,7 @@ public class MotionCamelEndLeftAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.searchHelper.findPreviousCamelEnd(editor.text(), caret.offset.point, operatorArguments.count1)
|
return injector.searchHelper.findPreviousCamelEnd(editor.text(), caret.offset, operatorArguments.count1)
|
||||||
?.toMotionOrError() ?: Motion.Error
|
?.toMotionOrError() ?: Motion.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ public class MotionCamelEndRightAction : MotionActionHandler.ForEachCaret() {
|
|||||||
argument: Argument?,
|
argument: Argument?,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Motion {
|
): Motion {
|
||||||
return injector.searchHelper.findNextCamelEnd(editor.text(), caret.offset.point + 1, operatorArguments.count1)
|
return injector.searchHelper.findNextCamelEnd(editor.text(), caret.offset + 1, operatorArguments.count1)
|
||||||
?.toMotionOrError() ?: Motion.Error
|
?.toMotionOrError() ?: Motion.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user