mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-18 19:24:55 +02:00
Compare commits
18 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
dbf26be1f0
|
|||
500756c86f
|
|||
5967979113
|
|||
358a13c22c
|
|||
64229d327a
|
|||
62d38eb4df
|
|||
4e15b65cec
|
|||
e4d31379f0
|
|||
195910b3a5
|
|||
10d476340f
|
|||
2c4ccc77b9
|
|||
4b0c2bce18
|
|||
a2d5adbc6c
|
|||
3b5b4ed84c
|
|||
a33addb6ea
|
|||
626b6ee0af
|
|||
f353f47987
|
|||
5410187bd5
|
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
|||||||
fetch-depth: 300
|
fetch-depth: 300
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --tags origin
|
run: git fetch --tags origin
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
|
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Update YouTrack
|
- name: Update YouTrack
|
||||||
run: ./gradlew --no-configuration-cache updateYoutrackOnCommit
|
run: ./gradlew updateYoutrackOnCommit
|
||||||
env:
|
env:
|
||||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
||||||
|
4
.github/workflows/integrationsTest.yml
vendored
4
.github/workflows/integrationsTest.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 300
|
fetch-depth: 300
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/kover.yml
vendored
4
.github/workflows/kover.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 300
|
fetch-depth: 300
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
6
.github/workflows/mergePr.yml
vendored
6
.github/workflows/mergePr.yml
vendored
@@ -20,17 +20,17 @@ jobs:
|
|||||||
fetch-depth: 50
|
fetch-depth: 50
|
||||||
# See end of file updateChangeslog.yml for explanation of this secret
|
# See end of file updateChangeslog.yml for explanation of this secret
|
||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
- name: Update authors
|
- name: Update authors
|
||||||
id: update_authors
|
id: update_authors
|
||||||
run: ./gradlew --no-configuration-cache updateMergedPr -PprId=${{ github.event.number }}
|
run: ./gradlew updateMergedPr -PprId=${{ github.event.number }}
|
||||||
env:
|
env:
|
||||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
6
.github/workflows/runUiOctopusTests.yml
vendored
6
.github/workflows/runUiOctopusTests.yml
vendored
@@ -15,7 +15,11 @@ jobs:
|
|||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||||
|
with:
|
||||||
|
# Not strictly necessary, but it may prevent rate limit
|
||||||
|
# errors especially on GitHub-hosted macos machines.
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Setup Gradle
|
- 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,7 +18,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||||
|
with:
|
||||||
|
# Not strictly necessary, but it may prevent rate limit
|
||||||
|
# errors especially on GitHub-hosted macos machines.
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Setup Gradle
|
- 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,7 +15,11 @@ jobs:
|
|||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||||
|
with:
|
||||||
|
# Not strictly necessary, but it may prevent rate limit
|
||||||
|
# errors especially on GitHub-hosted macos machines.
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Setup Gradle
|
- 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
Normal file
34
.github/workflows/updateAffectedRate.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
|
||||||
|
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
|
||||||
|
|
||||||
|
# This workflow syncs changes from the docs folder of IdeaVim to the IdeaVim.wiki repository
|
||||||
|
|
||||||
|
name: Update Affected Rate field on YouTrack
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 8 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'JetBrains/ideavim'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Fetch origin repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'adopt'
|
||||||
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
|
- name: Update YouTrack
|
||||||
|
run: ./gradlew scripts:updateAffectedRates
|
||||||
|
env:
|
||||||
|
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
6
.github/workflows/updateAuthors.yml
vendored
6
.github/workflows/updateAuthors.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
|||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --tags origin
|
run: git fetch --tags origin
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Update authors
|
- name: Update authors
|
||||||
id: update_authors
|
id: update_authors
|
||||||
run: ./gradlew --no-configuration-cache updateAuthors --stacktrace
|
run: ./gradlew updateAuthors --stacktrace
|
||||||
env:
|
env:
|
||||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
6
.github/workflows/updateChangelog.yml
vendored
6
.github/workflows/updateChangelog.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
|||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --tags origin
|
run: git fetch --tags origin
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
|
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Update changelog
|
- name: Update changelog
|
||||||
run: ./gradlew --no-configuration-cache updateChangelog
|
run: ./gradlew updateChangelog
|
||||||
env:
|
env:
|
||||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||||
|
|
||||||
|
4
.github/workflows/updateFormatting.yml
vendored
4
.github/workflows/updateFormatting.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
|||||||
fetch-depth: 50
|
fetch-depth: 50
|
||||||
# See end of file updateChangeslog.yml for explanation of this secret
|
# See end of file updateChangeslog.yml for explanation of this secret
|
||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -10,8 +10,6 @@
|
|||||||
!/.idea/runConfigurations
|
!/.idea/runConfigurations
|
||||||
!/.idea/codeStyles
|
!/.idea/codeStyles
|
||||||
!/.idea/vcs.xml
|
!/.idea/vcs.xml
|
||||||
!/.idea/misc.xml
|
|
||||||
!/.idea/.name
|
|
||||||
|
|
||||||
**/build/
|
**/build/
|
||||||
**/out/
|
**/out/
|
||||||
@@ -24,7 +22,7 @@
|
|||||||
.teamcity/*.iml
|
.teamcity/*.iml
|
||||||
|
|
||||||
# Generated by gradle task "generateGrammarSource"
|
# Generated by gradle task "generateGrammarSource"
|
||||||
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
|
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||||
# Generated JSONs for lazy classloading
|
# Generated JSONs for lazy classloading
|
||||||
/vim-engine/src/main/resources/ksp-generated
|
/vim-engine/src/main/resources/ksp-generated
|
||||||
|
1
.idea/.name
generated
1
.idea/.name
generated
@@ -1 +0,0 @@
|
|||||||
IdeaVim
|
|
22
.idea/misc.xml
generated
22
.idea/misc.xml
generated
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="EntryPointsManager">
|
|
||||||
<list size="3">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="com.intellij.vim.annotations.CommandOrMotion" />
|
|
||||||
<item index="1" class="java.lang.String" itemvalue="com.intellij.vim.annotations.ExCommand" />
|
|
||||||
<item index="2" class="java.lang.String" itemvalue="com.intellij.vim.annotations.VimscriptFunction" />
|
|
||||||
</list>
|
|
||||||
</component>
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
|
||||||
</component>
|
|
||||||
<component name="MavenProjectsManager">
|
|
||||||
<option name="originalFiles">
|
|
||||||
<list>
|
|
||||||
<option value="$PROJECT_DIR$/.teamcity/pom.xml" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
|
|
||||||
</project>
|
|
@@ -1,25 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
|
|
||||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
|
||||||
<ExternalSystemSettings>
|
|
||||||
<option name="executionName" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="externalSystemIdString" value="GRADLE" />
|
|
||||||
<option name="scriptParameters" value="" />
|
|
||||||
<option name="taskDescriptions">
|
|
||||||
<list />
|
|
||||||
</option>
|
|
||||||
<option name="taskNames">
|
|
||||||
<list>
|
|
||||||
<option value="runIdeSplitMode" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
<option name="vmOptions" value="" />
|
|
||||||
</ExternalSystemSettings>
|
|
||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
|
||||||
<RunAsTest>false</RunAsTest>
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -11,6 +11,6 @@
|
|||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
16
.teamcity/_Self/Constants.kt
vendored
16
.teamcity/_Self/Constants.kt
vendored
@@ -5,13 +5,13 @@ object Constants {
|
|||||||
const val EAP_CHANNEL = "eap"
|
const val EAP_CHANNEL = "eap"
|
||||||
const val DEV_CHANNEL = "Dev"
|
const val DEV_CHANNEL = "Dev"
|
||||||
|
|
||||||
const val GITHUB_TESTS = "2024.1.1"
|
const val GITHUB_TESTS = "2023.3.2"
|
||||||
const val NVIM_TESTS = "2024.1.1"
|
const val NVIM_TESTS = "2023.3.2"
|
||||||
const val PROPERTY_TESTS = "2024.1.1"
|
const val PROPERTY_TESTS = "2023.3.2"
|
||||||
const val LONG_RUNNING_TESTS = "2024.1.1"
|
const val LONG_RUNNING_TESTS = "2023.3.2"
|
||||||
const val QODANA_TESTS = "2024.1.1"
|
const val QODANA_TESTS = "2023.3.2"
|
||||||
const val RELEASE = "2024.1.1"
|
const val RELEASE = "2023.3.2"
|
||||||
|
|
||||||
const val RELEASE_DEV = "2024.1.1"
|
const val RELEASE_DEV = "2023.3.2"
|
||||||
const val RELEASE_EAP = "2024.1.1"
|
const val RELEASE_EAP = "2023.3.2"
|
||||||
}
|
}
|
||||||
|
4
.teamcity/_Self/Project.kt
vendored
4
.teamcity/_Self/Project.kt
vendored
@@ -11,7 +11,6 @@ import _Self.subprojects.GitHub
|
|||||||
import _Self.subprojects.OldTests
|
import _Self.subprojects.OldTests
|
||||||
import _Self.subprojects.Releases
|
import _Self.subprojects.Releases
|
||||||
import _Self.vcsRoots.GitHubPullRequest
|
import _Self.vcsRoots.GitHubPullRequest
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
|
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
||||||
|
|
||||||
@@ -22,11 +21,10 @@ object Project : Project({
|
|||||||
|
|
||||||
// VCS roots
|
// VCS roots
|
||||||
vcsRoot(GitHubPullRequest)
|
vcsRoot(GitHubPullRequest)
|
||||||
vcsRoot(ReleasesVcsRoot)
|
|
||||||
|
|
||||||
// Active tests
|
// Active tests
|
||||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
buildType(TestingBuildType("2024.1.1", "<default>"))
|
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
||||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
|
|
||||||
buildType(PropertyBased)
|
buildType(PropertyBased)
|
||||||
|
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.IdeaVimBuildType
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
|
||||||
|
|
||||||
object CreateNewReleaseBranchFromMaster : IdeaVimBuildType({
|
|
||||||
name = "EXP: Create new release branch from master"
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(DslContext.settingsRoot)
|
|
||||||
branchFilter = "+:<default>"
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
name = "Calculate next potential release version"
|
|
||||||
scriptContent = """
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Fetch all remote branches
|
|
||||||
git fetch --all
|
|
||||||
|
|
||||||
# Get a list of all branches matching the pattern releases/x.y.z
|
|
||||||
branches=${'$'}(git branch -r | grep -oE 'releases/[0-9]+\.[0-9]+\.x')
|
|
||||||
|
|
||||||
# If no matching branches are found, print a message and exit
|
|
||||||
if [[ -z "${'$'}branches" ]]; then
|
|
||||||
echo "No release branches found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find the largest release version
|
|
||||||
largest_release=${'$'}(echo "${'$'}branches" | sort -V | tail -n 1)
|
|
||||||
|
|
||||||
# Print the largest release
|
|
||||||
echo "Largest release branch: ${'$'}largest_release"
|
|
||||||
echo "##teamcity[setParameter name='env.POTENTIAL_VERSION' value='${'$'}largest_release']"
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
|
|
||||||
script {
|
|
||||||
name = "Show potential release version"
|
|
||||||
scriptContent = """
|
|
||||||
#!/bin/bash
|
|
||||||
echo "Calculated or user-provided parameter value is: %env.POTENTIAL_VERSION%"
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
params {
|
|
||||||
param("env.POTENTIAL_VERSION", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
sshAgent {
|
|
||||||
teamcitySshKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
@@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.IdeaVimBuildType
|
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
|
||||||
|
|
||||||
object PrintReleaseBranch : IdeaVimBuildType({
|
|
||||||
name = "EXP: Print release branch"
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(ReleasesVcsRoot)
|
|
||||||
branchFilter = "+:heads/releases/*"
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
|
|
||||||
script {
|
|
||||||
name = "Print current branch"
|
|
||||||
scriptContent = """
|
|
||||||
echo "Current branch is: %teamcity.build.branch%"
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
sshAgent {
|
|
||||||
teamcitySshKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
@@ -26,7 +26,7 @@ object PublishVimEngine : IdeaVimBuildType({
|
|||||||
|
|
||||||
vcs {
|
vcs {
|
||||||
root(DslContext.settingsRoot)
|
root(DslContext.settingsRoot)
|
||||||
branchFilter = "+:fleet"
|
branchFilter = "+:<default>"
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
checkoutMode = CheckoutMode.AUTO
|
||||||
}
|
}
|
||||||
|
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
@@ -1,106 +0,0 @@
|
|||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.Constants.EAP_CHANNEL
|
|
||||||
import _Self.Constants.RELEASE_EAP
|
|
||||||
import _Self.IdeaVimBuildType
|
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
|
||||||
|
|
||||||
object ReleaseEapFromBranch : IdeaVimBuildType({
|
|
||||||
name = "EXP: Publish EAP Build from branch"
|
|
||||||
description = "Build and publish EAP of IdeaVim plugin"
|
|
||||||
|
|
||||||
artifactRules = "build/distributions/*"
|
|
||||||
|
|
||||||
params {
|
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE_EAP)
|
|
||||||
password(
|
|
||||||
"env.ORG_GRADLE_PROJECT_publishToken",
|
|
||||||
"credentialsJSON:61a36031-4da1-4226-a876-b8148bf32bde",
|
|
||||||
label = "Password"
|
|
||||||
)
|
|
||||||
param("env.ORG_GRADLE_PROJECT_publishChannels", EAP_CHANNEL)
|
|
||||||
password(
|
|
||||||
"env.ORG_GRADLE_PROJECT_slackUrl",
|
|
||||||
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
|
|
||||||
label = "Slack Token"
|
|
||||||
)
|
|
||||||
password(
|
|
||||||
"env.YOUTRACK_TOKEN",
|
|
||||||
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
|
|
||||||
display = ParameterDisplay.HIDDEN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(ReleasesVcsRoot)
|
|
||||||
branchFilter = """
|
|
||||||
+:heads/(releases/*)
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
name = "Pull git tags"
|
|
||||||
scriptContent = "git fetch --tags origin"
|
|
||||||
}
|
|
||||||
script {
|
|
||||||
name = "Pull git history"
|
|
||||||
scriptContent = "git fetch --unshallow"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Calculate new eap version from branch"
|
|
||||||
tasks = "scripts:calculateNewEapVersionFromBranch"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Set TeamCity build number"
|
|
||||||
tasks = "scripts:setTeamCityBuildNumber"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Add release tag"
|
|
||||||
tasks = "scripts:addReleaseTag"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Publish plugin"
|
|
||||||
tasks = "publishPlugin"
|
|
||||||
}
|
|
||||||
script {
|
|
||||||
name = "Push changes to the repo"
|
|
||||||
scriptContent = """
|
|
||||||
branch=$(git branch --show-current)
|
|
||||||
echo current branch is ${'$'}branch
|
|
||||||
git push origin %build.number%
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "YouTrack post release actions"
|
|
||||||
tasks = "scripts:eapReleaseActions"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
sshAgent {
|
|
||||||
teamcitySshKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
failureConditions {
|
|
||||||
failOnMetricChange {
|
|
||||||
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
|
|
||||||
threshold = 5
|
|
||||||
units = BuildFailureOnMetric.MetricUnit.PERCENTS
|
|
||||||
comparison = BuildFailureOnMetric.MetricComparison.DIFF
|
|
||||||
compareTo = build {
|
|
||||||
buildRule = lastSuccessful()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
22
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
22
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@@ -19,6 +19,8 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
|
|||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
||||||
|
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
||||||
|
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
||||||
|
|
||||||
object ReleaseMajor : ReleasePlugin("major")
|
object ReleaseMajor : ReleasePlugin("major")
|
||||||
object ReleaseMinor : ReleasePlugin("minor")
|
object ReleaseMinor : ReleasePlugin("minor")
|
||||||
@@ -145,10 +147,10 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
name = "Run Integrations"
|
name = "Run Integrations"
|
||||||
tasks = "releaseActions"
|
tasks = "releaseActions"
|
||||||
}
|
}
|
||||||
// gradle {
|
gradle {
|
||||||
// name = "Slack Notification"
|
name = "Slack Notification"
|
||||||
// tasks = "slackNotification"
|
tasks = "slackNotification"
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
features {
|
features {
|
||||||
@@ -156,4 +158,16 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
teamcitySshKey = "IdeaVim ssh keys"
|
teamcitySshKey = "IdeaVim ssh keys"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failureConditions {
|
||||||
|
failOnMetricChange {
|
||||||
|
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
|
||||||
|
threshold = 5
|
||||||
|
units = BuildFailureOnMetric.MetricUnit.PERCENTS
|
||||||
|
comparison = BuildFailureOnMetric.MetricComparison.DIFF
|
||||||
|
compareTo = build {
|
||||||
|
buildRule = lastSuccessful()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
4
.teamcity/_Self/subprojects/GitHub.kt
vendored
4
.teamcity/_Self/subprojects/GitHub.kt
vendored
@@ -16,10 +16,10 @@ object GitHub : Project({
|
|||||||
name = "Pull Requests checks"
|
name = "Pull Requests checks"
|
||||||
description = "Automatic checking of GitHub Pull Requests"
|
description = "Automatic checking of GitHub Pull Requests"
|
||||||
|
|
||||||
buildType(GithubBuildType("clean test", "Tests"))
|
buildType(Github("clean test", "Tests"))
|
||||||
})
|
})
|
||||||
|
|
||||||
class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({
|
class Github(command: String, desc: String) : IdeaVimBuildType({
|
||||||
name = "GitHub Pull Requests $desc"
|
name = "GitHub Pull Requests $desc"
|
||||||
description = "Test GitHub pull requests $desc"
|
description = "Test GitHub pull requests $desc"
|
||||||
|
|
||||||
|
7
.teamcity/_Self/subprojects/Releases.kt
vendored
7
.teamcity/_Self/subprojects/Releases.kt
vendored
@@ -1,11 +1,8 @@
|
|||||||
package _Self.subprojects
|
package _Self.subprojects
|
||||||
|
|
||||||
import _Self.buildTypes.CreateNewReleaseBranchFromMaster
|
|
||||||
import _Self.buildTypes.PrintReleaseBranch
|
|
||||||
import _Self.buildTypes.PublishVimEngine
|
import _Self.buildTypes.PublishVimEngine
|
||||||
import _Self.buildTypes.ReleaseDev
|
import _Self.buildTypes.ReleaseDev
|
||||||
import _Self.buildTypes.ReleaseEap
|
import _Self.buildTypes.ReleaseEap
|
||||||
import _Self.buildTypes.ReleaseEapFromBranch
|
|
||||||
import _Self.buildTypes.ReleaseMajor
|
import _Self.buildTypes.ReleaseMajor
|
||||||
import _Self.buildTypes.ReleaseMinor
|
import _Self.buildTypes.ReleaseMinor
|
||||||
import _Self.buildTypes.ReleasePatch
|
import _Self.buildTypes.ReleasePatch
|
||||||
@@ -41,8 +38,4 @@ object Releases : Project({
|
|||||||
buildType(ReleaseEap)
|
buildType(ReleaseEap)
|
||||||
buildType(ReleaseDev)
|
buildType(ReleaseDev)
|
||||||
buildType(PublishVimEngine)
|
buildType(PublishVimEngine)
|
||||||
|
|
||||||
buildType(CreateNewReleaseBranchFromMaster)
|
|
||||||
buildType(PrintReleaseBranch)
|
|
||||||
buildType(ReleaseEapFromBranch)
|
|
||||||
})
|
})
|
||||||
|
13
.teamcity/_Self/vcsRoots/Releases.kt
vendored
13
.teamcity/_Self/vcsRoots/Releases.kt
vendored
@@ -1,13 +0,0 @@
|
|||||||
package _Self.vcsRoots
|
|
||||||
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.vcs.GitVcsRoot
|
|
||||||
|
|
||||||
object ReleasesVcsRoot : GitVcsRoot({
|
|
||||||
name = "IdeaVim Releases"
|
|
||||||
url = "git@github.com:JetBrains/ideavim.git"
|
|
||||||
branch = "refs/heads/master"
|
|
||||||
branchSpec = "+:refs/(*)"
|
|
||||||
authMethod = uploadedKey {
|
|
||||||
uploadedKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
})
|
|
@@ -5,15 +5,15 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
This patch script was generated by TeamCity on settings change in UI.
|
This patch script was generated by TeamCity on settings change in UI.
|
||||||
To apply the patch, change the buildType with id = 'ReleaseEapFromBranch'
|
To apply the patch, change the buildType with id = 'PublishVimEngine'
|
||||||
accordingly, and delete the patch script.
|
accordingly, and delete the patch script.
|
||||||
*/
|
*/
|
||||||
changeBuildType(RelativeId("ReleaseEapFromBranch")) {
|
changeBuildType(RelativeId("PublishVimEngine")) {
|
||||||
vcs {
|
vcs {
|
||||||
|
|
||||||
check(branchFilter == "+:heads/(releases/*)") {
|
check(branchFilter == "+:<default>") {
|
||||||
"Unexpected option value: branchFilter = $branchFilter"
|
"Unexpected option value: branchFilter = $branchFilter"
|
||||||
}
|
}
|
||||||
branchFilter = "heads/releases/*"
|
branchFilter = "+:fleet"
|
||||||
}
|
}
|
||||||
}
|
}
|
2
.teamcity/settings.kts
vendored
2
.teamcity/settings.kts
vendored
@@ -30,5 +30,5 @@ node (Plugins -> teamcity-configs -> teamcity-configs:generate),
|
|||||||
the 'Debug' option is available in the context menu for the task.
|
the 'Debug' option is available in the context menu for the task.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
version = "2024.03"
|
version = "2023.11"
|
||||||
project(_Self.Project)
|
project(_Self.Project)
|
||||||
|
20
AUTHORS.md
20
AUTHORS.md
@@ -503,26 +503,6 @@ Contributors:
|
|||||||
[![icon][github]](https://github.com/Parker7123)
|
[![icon][github]](https://github.com/Parker7123)
|
||||||
|
|
||||||
FilipParker
|
FilipParker
|
||||||
* [![icon][mail]](mailto:7138209+duhaesbaert@users.noreply.github.com)
|
|
||||||
[![icon][github]](https://github.com/duhaesbaert)
|
|
||||||
|
|
||||||
Eduardo Haesbaert
|
|
||||||
* [![icon][mail]](mailto:nikolaevsky.egor@gmail.com)
|
|
||||||
[![icon][github]](https://github.com/Aisper)
|
|
||||||
|
|
||||||
Egor Nikolaevsky
|
|
||||||
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
|
|
||||||
[![icon][github]](https://github.com/kun-codes)
|
|
||||||
|
|
||||||
Bishwa Saha,
|
|
||||||
* [![icon][mail]](mailto:alexfu@fastmail.com)
|
|
||||||
[![icon][github]](https://github.com/alexfu)
|
|
||||||
|
|
||||||
Alex Fu
|
|
||||||
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
|
|
||||||
[![icon][github]](https://github.com/LazyScaper)
|
|
||||||
|
|
||||||
Jake
|
|
||||||
|
|
||||||
Previous contributors:
|
Previous contributors:
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
IdeaVim project is licensed under MIT license except the following parts of it:
|
IdeaVim project is licensed under MIT license except the following parts of it:
|
||||||
|
|
||||||
|
* File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.
|
||||||
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
|
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
|
||||||
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
|
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.22")
|
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.19")
|
||||||
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")
|
||||||
|
@@ -13,7 +13,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
|||||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||||
import com.intellij.vim.processors.VimscriptFunctionProcessor
|
import com.intellij.vim.processors.VimscriptFunctionProcessor
|
||||||
|
|
||||||
class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
||||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
||||||
return VimscriptFunctionProcessor(environment)
|
return VimscriptFunctionProcessor(environment)
|
||||||
}
|
}
|
||||||
|
156
build.gradle.kts
156
build.gradle.kts
@@ -48,14 +48,14 @@ buildscript {
|
|||||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||||
|
|
||||||
// This is needed for jgit to connect to ssh
|
// This is needed for jgit to connect to ssh
|
||||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-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.12")
|
classpath("io.ktor:ktor-client-core:2.3.9")
|
||||||
classpath("io.ktor:ktor-client-cio:2.3.10")
|
classpath("io.ktor:ktor-client-cio:2.3.9")
|
||||||
classpath("io.ktor:ktor-client-auth:2.3.12")
|
classpath("io.ktor:ktor-client-auth:2.3.9")
|
||||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
classpath("io.ktor:ktor-client-content-negotiation:2.3.9")
|
||||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
||||||
|
|
||||||
// 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")
|
||||||
@@ -63,20 +63,36 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
antlr
|
||||||
java
|
java
|
||||||
kotlin("jvm") version "1.9.22"
|
kotlin("jvm") version "1.9.22"
|
||||||
application
|
application
|
||||||
id("java-test-fixtures")
|
id("java-test-fixtures")
|
||||||
|
|
||||||
id("org.jetbrains.intellij") version "1.17.3"
|
id("org.jetbrains.intellij") version "1.17.2"
|
||||||
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 "4.0.0"
|
id("com.dorongold.task-tree") version "2.1.1"
|
||||||
|
|
||||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ksp {
|
||||||
|
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
||||||
|
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
||||||
|
arg("ex_commands_file", "intellij_ex_commands.json")
|
||||||
|
arg("commands_file", "intellij_commands.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
||||||
|
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
|
||||||
|
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||||
|
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||||
|
tasks.named("kspTestKotlin").configure { enabled = false }
|
||||||
|
}
|
||||||
|
|
||||||
// Import variables from gradle.properties file
|
// Import variables from gradle.properties file
|
||||||
val javaVersion: String by project
|
val javaVersion: String by project
|
||||||
val kotlinVersion: String by project
|
val kotlinVersion: String by project
|
||||||
@@ -84,8 +100,8 @@ val ideaVersion: String by project
|
|||||||
val ideaType: String by project
|
val ideaType: String by project
|
||||||
val downloadIdeaSources: String by project
|
val downloadIdeaSources: String by project
|
||||||
val instrumentPluginCode: String by project
|
val instrumentPluginCode: String by project
|
||||||
|
val antlrVersion: String by project
|
||||||
val remoteRobotVersion: String by project
|
val remoteRobotVersion: String by project
|
||||||
val splitModeVersion: String by project
|
|
||||||
|
|
||||||
val publishChannels: String by project
|
val publishChannels: String by project
|
||||||
val publishToken: String by project
|
val publishToken: String by project
|
||||||
@@ -93,8 +109,6 @@ val publishToken: String by project
|
|||||||
val slackUrl: String by project
|
val slackUrl: String by project
|
||||||
val youtrackToken: String by project
|
val youtrackToken: String by project
|
||||||
|
|
||||||
val releaseType: String? by project
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
|
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
|
||||||
@@ -103,10 +117,12 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(project(":vim-engine"))
|
api(project(":vim-engine"))
|
||||||
ksp(project(":annotation-processors"))
|
ksp(project(":annotation-processors"))
|
||||||
compileOnly(project(":annotation-processors"))
|
implementation(project(":annotation-processors"))
|
||||||
|
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
||||||
compileOnly("org.jetbrains:annotations:24.1.0")
|
compileOnly("org.jetbrains:annotations:24.1.0")
|
||||||
|
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
|
||||||
|
antlr("org.antlr:antlr4:$antlrVersion")
|
||||||
|
|
||||||
// --------- Test dependencies ----------
|
// --------- Test dependencies ----------
|
||||||
|
|
||||||
@@ -125,14 +141,14 @@ 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.3.1")
|
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
@@ -143,8 +159,6 @@ configurations {
|
|||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
|
||||||
|
|
||||||
// Set teamcity env variable locally to run additional tests for leaks.
|
// Set teamcity env variable locally to run additional tests for leaks.
|
||||||
// By default, this test runs on TC only, but this test doesn't take a lot of time,
|
// By default, this test runs on TC only, but this test doesn't take a lot of time,
|
||||||
// so we can turn it on for local development
|
// so we can turn it on for local development
|
||||||
@@ -157,9 +171,6 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
// CodeQL can't resolve the 'by project' property, so we need to give it a hint. This is the minimum version we need
|
|
||||||
// so doesn't have to match exactly
|
|
||||||
// Hint for the CodeQL autobuilder: sourceCompatibility = 17
|
|
||||||
sourceCompatibility = javaVersion
|
sourceCompatibility = javaVersion
|
||||||
targetCompatibility = javaVersion
|
targetCompatibility = javaVersion
|
||||||
|
|
||||||
@@ -185,10 +196,6 @@ tasks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runIde {
|
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadRobotServerPlugin {
|
downloadRobotServerPlugin {
|
||||||
version.set(remoteRobotVersion)
|
version.set(remoteRobotVersion)
|
||||||
}
|
}
|
||||||
@@ -199,33 +206,11 @@ tasks {
|
|||||||
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
|
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
|
||||||
systemProperty("jb.consents.confirmation.enabled", "false")
|
systemProperty("jb.consents.confirmation.enabled", "false")
|
||||||
systemProperty("ide.show.tips.on.startup.default.value", "false")
|
systemProperty("ide.show.tips.on.startup.default.value", "false")
|
||||||
|
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add plugin open API sources to the plugin ZIP
|
runIde {
|
||||||
val createOpenApiSourceJar by registering(Jar::class) {
|
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||||
// Java sources
|
|
||||||
from(sourceSets.main.get().java) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
|
||||||
}
|
|
||||||
from(project(":vim-engine").sourceSets.main.get().java) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
|
||||||
}
|
|
||||||
// Kotlin sources
|
|
||||||
from(kotlin.sourceSets.main.get().kotlin) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
|
||||||
}
|
|
||||||
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
|
||||||
}
|
|
||||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
|
||||||
archiveClassifier.set("src")
|
|
||||||
}
|
|
||||||
|
|
||||||
buildPlugin {
|
|
||||||
dependsOn(createOpenApiSourceJar)
|
|
||||||
from(createOpenApiSourceJar) { into("lib/src") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +221,7 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
explicitApi()
|
||||||
jvmToolchain {
|
jvmToolchain {
|
||||||
languageVersion.set(JavaLanguageVersion.of(javaVersion))
|
languageVersion.set(JavaLanguageVersion.of(javaVersion))
|
||||||
}
|
}
|
||||||
@@ -278,11 +264,56 @@ 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 {
|
||||||
|
maxHeapSize = "128m"
|
||||||
|
arguments.addAll(listOf("-package", "com.maddyhome.idea.vim.vimscript.parser.generated", "-visitor"))
|
||||||
|
outputDirectory = file("src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
named("compileKotlin") {
|
||||||
|
dependsOn("generateGrammarSource")
|
||||||
|
}
|
||||||
|
named("compileTestKotlin") {
|
||||||
|
dependsOn("generateTestGrammarSource")
|
||||||
|
}
|
||||||
|
named("compileTestFixturesKotlin") {
|
||||||
|
dependsOn("generateTestFixturesGrammarSource")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add plugin open API sources to the plugin ZIP
|
||||||
|
val createOpenApiSourceJar by registering(Jar::class) {
|
||||||
|
dependsOn("generateGrammarSource")
|
||||||
|
// Java sources
|
||||||
|
from(sourceSets.main.get().java) {
|
||||||
|
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||||
|
}
|
||||||
|
from(project(":vim-engine").sourceSets.main.get().java) {
|
||||||
|
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||||
|
}
|
||||||
|
// Kotlin sources
|
||||||
|
from(kotlin.sourceSets.main.get().kotlin) {
|
||||||
|
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||||
|
}
|
||||||
|
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
||||||
|
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||||
|
}
|
||||||
|
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||||
|
archiveClassifier.set("src")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPlugin {
|
||||||
|
dependsOn(createOpenApiSourceJar)
|
||||||
|
from(createOpenApiSourceJar) { into("lib/src") }
|
||||||
}
|
}
|
||||||
|
|
||||||
patchPluginXml {
|
patchPluginXml {
|
||||||
// Don't forget to update plugin.xml
|
// Don't forget to update plugin.xml
|
||||||
sinceBuild.set("241.15989.150")
|
sinceBuild.set("233.11799.67")
|
||||||
|
|
||||||
changeNotes.set(
|
changeNotes.set(
|
||||||
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
|
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
|
||||||
@@ -290,21 +321,14 @@ tasks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ksp {
|
// --- Tests
|
||||||
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
|
||||||
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
|
||||||
arg("ex_commands_file", "intellij_ex_commands.json")
|
|
||||||
arg("commands_file", "intellij_commands.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEvaluate {
|
tasks {
|
||||||
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
test {
|
||||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
useJUnitPlatform()
|
||||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
}
|
||||||
tasks.named("kspTestKotlin").configure { enabled = false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Changelog
|
// --- Changelog
|
||||||
|
|
||||||
changelog {
|
changelog {
|
||||||
@@ -430,8 +454,6 @@ val fixVersionsElementType = "VersionBundleElement"
|
|||||||
tasks.register("releaseActions") {
|
tasks.register("releaseActions") {
|
||||||
group = "other"
|
group = "other"
|
||||||
doLast {
|
doLast {
|
||||||
if (releaseType == "patch") return@doLast
|
|
||||||
|
|
||||||
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
|
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
|
||||||
if (tickets.isNotEmpty()) {
|
if (tickets.isNotEmpty()) {
|
||||||
println("Updating statuses for tickets: $tickets")
|
println("Updating statuses for tickets: $tickets")
|
||||||
|
@@ -130,25 +130,7 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
|
|||||||
|
|
||||||
### Instructions
|
### Instructions
|
||||||
|
|
||||||
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
|
https://github.com/terryma/vim-multiple-cursors/blob/master/doc/multiple_cursors.txt
|
||||||
|
|
||||||
```
|
|
||||||
" Remap multiple-cursors shortcuts to match terryma/vim-multiple-cursors
|
|
||||||
nmap <C-n> <Plug>NextWholeOccurrence
|
|
||||||
xmap <C-n> <Plug>NextWholeOccurrence
|
|
||||||
nmap g<C-n> <Plug>NextOccurrence
|
|
||||||
xmap g<C-n> <Plug>NextOccurrence
|
|
||||||
xmap <C-x> <Plug>SkipOccurrence
|
|
||||||
xmap <C-p> <Plug>RemoveOccurrence
|
|
||||||
|
|
||||||
" Note that the default <A-n> and g<A-n> shortcuts don't work on Mac due to dead keys.
|
|
||||||
" <A-n> is used to enter accented text e.g. ñ
|
|
||||||
" Feel free to pick your own mappings that are not affected. I like to use <leader>
|
|
||||||
nmap <leader><C-n> <Plug>AllWholeOccurrences
|
|
||||||
xmap <leader><C-n> <Plug>AllWholeOccurrences
|
|
||||||
nmap <leader>g<C-n> <Plug>AllOccurrences
|
|
||||||
xmap <leader>g<C-n> <Plug>AllOccurrences
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ Plug 'nerdtree'
|
|||||||
### Preview
|
### Preview
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for the preview</summary>
|
<summary>Click to the the preview</summary>
|
||||||
<img src="images/nerdtree.gif" alt="NERDTree example"/>
|
<img src="images/nerdtree.gif" alt="NERDTree example"/>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -40,33 +40,30 @@ Plug 'nerdtree'
|
|||||||
- `:NERDTreeFind`
|
- `:NERDTreeFind`
|
||||||
- `:NERDTreeRefreshRoot`
|
- `:NERDTreeRefreshRoot`
|
||||||
|
|
||||||
| Key | Description | Map Setting |
|
| Key | Description | Map Setting |
|
||||||
|---------|--------------------------------------------------------|--------------------------------|
|
|---------|---------------------------------------------------------|--------------------------------|
|
||||||
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
||||||
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
||||||
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
||||||
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
||||||
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
||||||
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
||||||
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
||||||
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
||||||
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
||||||
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
||||||
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
||||||
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
||||||
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
||||||
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
||||||
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
||||||
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
||||||
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
||||||
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
||||||
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
||||||
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
||||||
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
||||||
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
||||||
| `d` | Delete file or directory | `g:NERDTreeMapDelete` |
|
|
||||||
| `n` | Create File | `g:NERDTreeMapNewFile` |
|
|
||||||
| `N` | Create Directory | `g:NERDTreeMapNewDir` |
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
|
@@ -1,268 +1,140 @@
|
|||||||
List of Supported Options
|
List of Supported Set Commands
|
||||||
=========================
|
==============================
|
||||||
|
|
||||||
The following options can be set with the `:set`, `:setglobal` and `:setlocal` commands.
|
The following `:set` commands can appear in `~/.ideavimrc` or be set manually in the command mode:
|
||||||
They can be added to the `~/.ideavimrc` file, or set manually in Command-line mode.
|
|
||||||
For more details of each option, please see the Vim documentation.
|
|
||||||
Every effort is made to make these options compatible with Vim behaviour.
|
|
||||||
However, some differences are inevitable.
|
|
||||||
|
|
||||||
```
|
'clipboard' 'cb' clipboard options
|
||||||
'clipboard' 'cb' Defines clipboard behavioue
|
Standard clipboard options plus
|
||||||
A comma-separated list of words to control clipboard behaviour:
|
|
||||||
unnamed The clipboard register '*' is used instead of the
|
|
||||||
unnamed register
|
|
||||||
unnamedplus The clipboard register '+' is used instead of the
|
|
||||||
unnamed register
|
|
||||||
ideaput Uses the IDEs own paste implementation for put
|
|
||||||
operations rather than simply inserting the text
|
|
||||||
|
|
||||||
'digraph' 'dg' Enable using <BS> to enter digraphs in Insert mode
|
`ideaput` (default on) - IdeaVim ONLY
|
||||||
'gdefault' 'gd' The ":substitute" flag 'g' is by default
|
enable native idea paste action for put operations
|
||||||
'guicursor' 'gcr' Controls the shape of the cursor for different modes
|
|
||||||
'history' 'hi' Number of command-lines that are remembered
|
|
||||||
'hlsearch' 'hls' Highlight matches with the last search pattern
|
|
||||||
'ignorecase' 'ic' Ignore case in search patterns
|
|
||||||
'incsearch' 'is' Show where search pattern typed so far matches
|
|
||||||
'iskeyword' 'isk' Defines keywords for commands like 'w', '*', etc.
|
|
||||||
'keymodel' 'km' Controls selection behaviour with special keys
|
|
||||||
List of comma separated words, which enable special things that keys
|
|
||||||
can do. These values can be used:
|
|
||||||
startsel Using a shifted special key starts selection (either
|
|
||||||
Select mode or Visual mode, depending on "key" being
|
|
||||||
present in 'selectmode')
|
|
||||||
stopsel Using a NOT-shifted special key stops selection.
|
|
||||||
Automatically enables `stopselect` and `stopvisual`
|
|
||||||
stopselect Using a NOT-shifted special key stops select mode
|
|
||||||
and removes selection - IdeaVim ONLY
|
|
||||||
stopvisual Using a NOT-shifted special key stops visual mode
|
|
||||||
and removes selection - IdeaVim ONLY
|
|
||||||
continueselect Using a shifted arrow key doesn't start selection,
|
|
||||||
but in select mode acts like startsel is enabled
|
|
||||||
- IdeaVim ONLY
|
|
||||||
continuevisual Using a shifted arrow key doesn't start selection,
|
|
||||||
but in visual mode acts like startsel is enabled
|
|
||||||
- IdeaVim ONLY
|
|
||||||
|
|
||||||
Special keys in this context are the cursor keys, <End>, <Home>,
|
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||||
<PageUp> and <PageDown>.
|
'gdefault' 'gd' the ":substitute" flag 'g' is by default
|
||||||
|
'history' 'hi' number of command-lines that are remembered
|
||||||
|
'hlsearch' 'hls' highlight matches with the last search pattern
|
||||||
|
'ignorecase' 'ic' ignore case in search patterns
|
||||||
|
'iskeyword' 'isk' defines keywords for commands like 'w', '*', etc.
|
||||||
|
'incsearch' 'is' show where search pattern typed so far matches
|
||||||
|
|
||||||
'matchpairs' 'mps' Pairs of characters that "%" can match
|
`keymodel` `km` String (default "continueselect,stopselect")
|
||||||
'maxmapdepth' 'mmd' Maximum depth of mappings
|
|
||||||
'more' 'more' When on, listings pause when the whole screen is filled
|
|
||||||
'nrformats' 'nf' Number formats recognized for CTRL-A command
|
|
||||||
'operatorfunc' 'opfunc' Name of a function to call with the g@ operator
|
|
||||||
'scroll' 'scr' Number of lines to scroll with CTRL-U and CTRL-D
|
|
||||||
'selection' 'sel' What type of selection to use
|
|
||||||
'selectmode' 'slm' Controls when to start Select mode instead of Visual
|
|
||||||
This is a comma-separated list of words:
|
|
||||||
|
|
||||||
mouse When using the mouse
|
List of comma separated words, which enable special things that keys
|
||||||
key When using shifted special[1] keys
|
can do. These values can be used:
|
||||||
cmd When using "v", "V", or <C-V>
|
startsel Using a shifted special[1] key starts selection (either
|
||||||
ideaselection When IDE sets a selection - IdeaVim ONLY
|
Select mode or Visual mode, depending on "key" being
|
||||||
(e.g.: extend selection, wrap with while, etc.)
|
present in 'selectmode').
|
||||||
|
stopsel Using a NOT-shifted special[1] key stops selection.
|
||||||
|
Automatically enables `stopselect` and `stopvisual`
|
||||||
|
stopselect Using a NOT-shifted special[1] key stops - IdeaVim ONLY
|
||||||
|
select mode and removes selection.
|
||||||
|
stopvisual Using a NOT-shifted special[1] key stops - IdeaVim ONLY
|
||||||
|
visual mode and removes selection.
|
||||||
|
continueselect Using a shifted arrow key doesn't - IdeaVim ONLY
|
||||||
|
start selection, but in select mode
|
||||||
|
acts like startsel is enabled
|
||||||
|
continuevisual Using a shifted arrow key doesn't - IdeaVim ONLY
|
||||||
|
start selection, but in visual mode
|
||||||
|
acts like startsel is enabled
|
||||||
|
|
||||||
'shell' 'sh' The shell to use to execute commands with ! and :!
|
'matchpairs' 'mps' pairs of characters that "%" can match
|
||||||
'shellcmdflag' 'shcf' The command flag passed to the shell
|
'maxmapdepth' 'mmd' Maximum depth of mappings
|
||||||
'shellxescape' 'sxe' The characters to be escaped when calling a shell
|
'more' 'more' When on, listings pause when the whole screen is filled.
|
||||||
'shellxquote' 'sxq' The quote character to use in a shell command
|
'nrformats' 'nf' number formats recognized for CTRL-A command
|
||||||
'showcmd' 'sc' Show (partial) command in the status bar
|
'number' 'nu' print the line number in front of each line
|
||||||
'showmode' 'smd' Show the current mode in the status bar
|
'relativenumber' 'rnu' show the line number relative to the line with
|
||||||
'smartcase' 'scs' Use case sensitive search if any character in the
|
the cursor
|
||||||
pattern is uppercase
|
'scroll' 'scr' lines to scroll with CTRL-U and CTRL-D
|
||||||
'startofline' 'sol' When on, some commands move the cursor to the first
|
'scrolljump' 'sj' minimum number of lines to scroll
|
||||||
non-blank of the line
|
'scrolloff' 'so' minimum number of lines above and below the cursor
|
||||||
When off, the cursor is kept in the same column
|
'selection' 'sel' what type of selection to use
|
||||||
(if possible)
|
|
||||||
'timeout' 'to' Use timeout for mapped key sequences
|
|
||||||
'timeoutlen' 'tm' Timeout duration for a mapped key sequence
|
|
||||||
'viminfo' 'vi' Information to remember after restart
|
|
||||||
'virtualedit' 've' Placement of the cursor where there is no actual text
|
|
||||||
A comma-separated list of these words:
|
|
||||||
block Allow virtual editing in Visual mode (not supported)
|
|
||||||
insert Allow virtual editing in Insert mode (not supported)
|
|
||||||
all Allow virtual editing in all modes (not supported)
|
|
||||||
onemore Allow the cursor to move just past the end of the line
|
|
||||||
|
|
||||||
'visualbell' 'vb' When on, prevents beeping on error
|
`selectmode` `slm` String (default "")
|
||||||
'whichwrap' 'ww' Which keys that move the cursor left/right can wrap to
|
|
||||||
other lines
|
|
||||||
A comma-separated list of these flags:
|
|
||||||
char key modes
|
|
||||||
b <BS> Normal and Visual
|
|
||||||
s <Space> Normal and Visual
|
|
||||||
h "h" Normal and Visual
|
|
||||||
l "l" Normal and Visual
|
|
||||||
< <Left> Normal and Visual
|
|
||||||
> <Right> Normal and Visual
|
|
||||||
~ "~" Normal
|
|
||||||
[ <Left> Insert and Replace
|
|
||||||
] <Right> Insert and Replace
|
|
||||||
|
|
||||||
'wrapscan' 'ws' Search will wrap around the end of file
|
This is a comma-separated list of words, which specify when to start
|
||||||
```
|
Select mode instead of Visual mode, when a selection is started.
|
||||||
|
Possible values:
|
||||||
|
mouse when using the mouse
|
||||||
|
key when using shifted special[1] keys
|
||||||
|
cmd when using "v", "V", or <C-V>
|
||||||
|
ideaselection when IDE sets a selection - IdeaVim ONLY
|
||||||
|
(examples: extend selection, wrap with while, etc.)
|
||||||
|
|
||||||
## IdeaVim options mapped to IntelliJ-based IDE settings
|
`startofline` `sol` When "on" some commands move the cursor to the first non-blank of the line.
|
||||||
|
When off the cursor is kept in the same column (if possible).
|
||||||
|
|
||||||
IdeaVim provides its own implementation for handling scroll jump and offset, even though IntelliJ-based IDEs have similar functionality (there are differences in behaviour).
|
'showmode' 'smd' message on the status line to show current mode
|
||||||
When IdeaVim is hosted in an IntelliJ-based IDE (but not JetBrains Fleet), the following options map to the equivalent IDE settings:
|
'showcmd' 'sc' show (partial) command in the status bar
|
||||||
|
'sidescroll' 'ss' minimum number of columns to scroll horizontally
|
||||||
|
'sidescrolloff' 'siso' min. number of columns to left and right of cursor
|
||||||
|
'smartcase' 'scs' no ignore case when pattern is uppercase
|
||||||
|
'timeout' 'to' use timeout for mapped key sequences
|
||||||
|
'timeoutlen' 'tm' timeout duration for a mapped key sequence
|
||||||
|
'undolevels' 'ul' maximum number of changes that can be undone
|
||||||
|
'viminfo' 'vi' information to remember after restart
|
||||||
|
'visualbell' 'vb' use visual bell instead of beeping
|
||||||
|
'wrapscan' 'ws' searches wrap around the end of file
|
||||||
|
|
||||||
```
|
|
||||||
'scrolljump' 'sj' Minimal number of lines to scroll
|
|
||||||
'scrolloff' 'so' Minimal number of lines above and below the cursor
|
|
||||||
'sidescroll' 'ss' Minimal number of columns to scroll horizontally
|
|
||||||
'sidescrolloff' 'siso' Minimal number of columns to left and right of cursor
|
|
||||||
```
|
|
||||||
|
|
||||||
## IdeaVim options for IntelliJ-based IDE features
|
|
||||||
|
|
||||||
Some Vim features cannot be implemented by IdeaVim, and must be implemented by the host IDE, such as showing whitespace and line numbers, and enabling soft-wrap.
|
IdeaVim only commands:
|
||||||
The following options modify equivalent settings and features implemented by IntelliJ-based IDEs.
|
|
||||||
|
|
||||||
There is some mismatch when trying to map Vim options, most of which are local options, to IDE settings, which are mostly global-local.
|
`ideamarks` `ideamarks` Boolean (default true)
|
||||||
The Vim option will always reflect the effective value of the IDE setting for the current editor, and modifying the Vim option will update the local value of the IDE setting.
|
|
||||||
The default value of the Vim option set during startup is not passed to the IDE setting.
|
|
||||||
|
|
||||||
If the IDE setting has a way to modify the local value, such as entries in the _View | Active Editor_ menu, then changing this will update the current editor and be reflected in the Vim option value.
|
If true, creation of global mark will trigger creation of IDE's bookmark
|
||||||
If the IDE setting can only modify its global setting in the main _Settings_ dialog, this change does not always update the current editor (because the local IDE setting has been modified and takes precedence).
|
and vice versa.
|
||||||
|
|
||||||
IdeaVim tries to make this work more naturally by updating the editor and local Vim option when a global value changes unless the Vim option has been explicitly set in Command-line mode.
|
`idearefactormode` `idearefactormode` String(default "select")
|
||||||
|
|
||||||
In other words, if the local Vim value is explicitly set for a window or buffer, interactively, then it should not be reset.
|
Define the mode that would be enabled during
|
||||||
If the Vim option was explicitly set in `~/.ideavimrc` however, then the value will be reset, because this can be viewed as a "global" value - set once and applied to subsequently opened windows.
|
the refactoring (renaming, live template, introduce variable, etc)
|
||||||
(This should not be confused with Vim's concept of global options, which are mainly used to initialise new windows.)
|
|
||||||
|
|
||||||
The local Vim option can always be reset to the global IDE setting value by resetting the Vim option to default with the `:set {option}&` syntax.
|
Use one of the following values:
|
||||||
|
- keep - keep the mode that was enabled before starting a refactoring
|
||||||
|
- select - start refactoring in select mode
|
||||||
|
- visual - start refactoring in visual mode
|
||||||
|
|
||||||
```
|
This option has effect if you are in normal, insert or replace mode before refactoring start.
|
||||||
'bomb' 'bomb' Add or remove a byte order mark (BOM) to the
|
Visual or select mode are not changed.
|
||||||
current file. Unlike Vim, the file is modified
|
|
||||||
immediately, and not when saved
|
|
||||||
'breakindent' 'bri' Indent soft wrapped lines to match the first
|
|
||||||
line's indent
|
|
||||||
'colorcolumn' 'cc' Maps to IntelliJ's visual guide columns
|
|
||||||
'cursorline' 'cul' Highlight the line containing the cursor
|
|
||||||
'fileencoding' 'fenc' Change the encoding of the current file. The file
|
|
||||||
is modified and written immediately, rather than
|
|
||||||
waiting to be saved
|
|
||||||
Note that the names of the encoding might not
|
|
||||||
match Vim's known names
|
|
||||||
'fileformat' 'ff' Change the file format - dos, unix or mac
|
|
||||||
The file is modified immediately, rather than
|
|
||||||
when saved
|
|
||||||
'list' 'list' Show whitespace. Maps to the editor's local
|
|
||||||
setting in the View | Active Editor menu
|
|
||||||
'number' 'nu' Show line numbers. Maps to the editor's local
|
|
||||||
setting in the View | Active Editor menu
|
|
||||||
'relativenumber' 'rnu' Show line numbers relative to the current line
|
|
||||||
'textwidth' 'tw' Set the column at which text is automatically
|
|
||||||
wrapped
|
|
||||||
'wrap' 'wrap' Enable soft-wraps. Maps to the editor's local
|
|
||||||
setting in the View | Active Editor menu
|
|
||||||
```
|
|
||||||
|
|
||||||
## IdeaVim only options
|
|
||||||
|
|
||||||
These options are IdeaVim only, and not supported by Vim.
|
`ideajoin` `ideajoin` Boolean (default false)
|
||||||
They control integration with the host IDE.
|
|
||||||
Unless otherwise stated, these options do not have abbreviations.
|
|
||||||
|
|
||||||
```
|
If true, join command will be performed via IDE
|
||||||
'ideacopypreprocess' boolean (default off)
|
See wiki/`ideajoin` examples
|
||||||
global or local to buffer
|
|
||||||
When enabled, the IDE will run custom copy pre-processors over text
|
|
||||||
copied to registers. These pre-processors can perform transformations
|
|
||||||
on the text, such as converting escape characters in a string literal
|
|
||||||
into the actual control characters in a Java file.
|
|
||||||
|
|
||||||
This is not usually the expected behaviour, so this option's default
|
`ideastatusicon` `ideastatusicon` String(default "enabled")
|
||||||
value is off. The equivalent processing for paste is controlled by the
|
|
||||||
"ideaput" value to the 'clipboard' option.
|
|
||||||
|
|
||||||
'ideaglobalmode' boolean (default off)
|
Define the behavior of IdeaVim icon in the status bar.
|
||||||
global
|
|
||||||
This option will cause IdeaVim to share a single mode across all open
|
|
||||||
windows. In other words, entering Insert mode in one window will
|
|
||||||
enable Insert mode in all windows.
|
|
||||||
|
|
||||||
'ideajoin' boolean (default off)
|
Use one of the following values:
|
||||||
global or local to buffer
|
- enabled - icon is shown in the status bar
|
||||||
When enabled, join commands will be handled by the IDE's "smart join"
|
- gray - use the gray version of the icon
|
||||||
feature. The IDE can change syntax when joining lines, such as merging
|
- disabled - hide the icon
|
||||||
string literals or if statements. See the wiki for more examples. Not
|
|
||||||
all languages support smart join functionality.
|
|
||||||
|
|
||||||
'ideamarks' boolean (default on)
|
`ideawrite` `ideawrite` String (default "all")
|
||||||
global
|
"file" or "all". Defines the behaviour of ":w" command.
|
||||||
Maps Vim's global marks to IDE bookmarks.
|
Value "all" enables execution of ":wa" (save all) command on ":w" (save).
|
||||||
|
This feature exists because some IJ options like "Prettier on save" or "ESlint on save"
|
||||||
|
work only with "save all" action. If this option is set to "all", these actions work
|
||||||
|
also with ":w" command.
|
||||||
|
|
||||||
'idearefactormode' string (default "select")
|
`lookupkeys` `lookupkeys` List of strings
|
||||||
global or local to buffer
|
|
||||||
Specifies the mode to be used when a refactoring selects text to be
|
|
||||||
edited (e.g. renaming, live template fields, introduce variable, etc):
|
|
||||||
keep Keep the current mode
|
|
||||||
select Switch to Select mode
|
|
||||||
visual Switch to Visual mode
|
|
||||||
|
|
||||||
This option is only used when the refactoring is started in Normal,
|
List of keys that should be processed by the IDE during the active lookup (autocompletion).
|
||||||
Insert or Replace mode. Visual or Select modes are not changed.
|
For example, <Tab> and <Enter> are used by the IDE to finish the lookup,
|
||||||
|
but <C-W> should be passed to IdeaVim.
|
||||||
|
Default value:
|
||||||
|
"<Tab>", "<Down>", "<Up>", "<Enter>", "<Left>", "<Right>",
|
||||||
|
"<C-Down>", "<C-Up>", "<PageUp>", "<PageDown>",
|
||||||
|
"<C-J>", "<C-Q>"
|
||||||
|
|
||||||
'ideastatusicon' string (default "enabled")
|
`ideavimsupport` `ideavimsupport` List of strings (default "dialog")
|
||||||
global
|
|
||||||
This option controls the behaviour and appearance of the IdeaVim icon
|
|
||||||
in the status bar:
|
|
||||||
enabled Show the icon in the status bar
|
|
||||||
gray Show the gray version of the icon
|
|
||||||
disabled Hide the icon
|
|
||||||
|
|
||||||
'ideavimsupport' string (default "dialog")
|
Define the list of additional buffers where IdeaVim is enabled.
|
||||||
global
|
|
||||||
A comma-separated list of additional buffers or locations where
|
|
||||||
IdeaVim should be enabled:
|
|
||||||
dialog Enable IdeaVim in editors hosted in dialogs
|
|
||||||
singleline Enable IdeaVim in single line editors (not recommended)
|
|
||||||
|
|
||||||
The IDE's editor component can be used in many places, such as VCS
|
- dialog - enable IdeaVim in dialogs
|
||||||
commit tool window, or inside dialogs, and even as single line fields.
|
- singleline - enable IdeaVim in single line editors (not suggested)
|
||||||
|
|
||||||
'ideawrite' string (default "all")
|
----------
|
||||||
global
|
[1] - cursor keys, <End>, <Home>, <PageUp> and <PageDown>
|
||||||
This option defines the behaviour of the :w command:
|
|
||||||
file Save the current file only
|
|
||||||
all The :w command works like :wa and invokes the Save All
|
|
||||||
IDE action. This allows options such as "Prettier on
|
|
||||||
save" or "ESlint on save" to work with the :w command,
|
|
||||||
but means all files are saved.
|
|
||||||
|
|
||||||
'lookupkeys' string (default "<Tab>,<Down>,<Up>,<Enter>,
|
|
||||||
<Left>,<Right>,<C-Down>,<C-Up>,
|
|
||||||
<PageUp>,<PageDown>, <C-J>,<C-Q>")
|
|
||||||
global
|
|
||||||
Comma-separated list of keys that should be processed by the IDE while
|
|
||||||
a code completion lookup popup is active. For example, <Tab> and
|
|
||||||
<Enter> are used by the IDE to complete the lookup and insert text,
|
|
||||||
but <C-W> should be passed IdeaVim to continue editing the text.
|
|
||||||
|
|
||||||
'trackactionids' boolean (default off)
|
|
||||||
global
|
|
||||||
When on, IdeaVim will try to track the current IDE action and display
|
|
||||||
the action name in a notification. This action ID can then be used in
|
|
||||||
a mapping to the action in the form <Action>(...).
|
|
||||||
|
|
||||||
'visualdelay' number (default 100)
|
|
||||||
global
|
|
||||||
This option specifies the delay, in milliseconds before converting an
|
|
||||||
IDE selection into Visual mode.
|
|
||||||
|
|
||||||
Some IDE features make a selection to help modify text (e.g. backspace
|
|
||||||
in Python or Yaml selects an indent and invokes the "remove selection"
|
|
||||||
action). IdeaVim listens for changes in selection to switch to Visual
|
|
||||||
mode, and will return to Normal mode when the selection is removed,
|
|
||||||
even if originally in Insert mode.
|
|
||||||
|
|
||||||
By waiting before converting to Visual mode, temporary selections can
|
|
||||||
be ignored and the current Vim mode maintained.
|
|
||||||
|
|
||||||
It is not expected that this value will need to be changed.
|
|
||||||
```
|
|
||||||
|
@@ -8,30 +8,18 @@
|
|||||||
|
|
||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
|
|
||||||
# ideaVersion is the version of the IDE that will be added as a compile-time dependency. The format can be either
|
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||||
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
|
ideaVersion=2023.3.3
|
||||||
# resolved against the configured repositories, which by default includes Maven releases and snapshots, the CDN used to
|
|
||||||
# download consumer releases, the plugin marketplace and so on.
|
|
||||||
# You can find an example list of all CDN based versions for IDEA Community here:
|
|
||||||
# https://data.services.jetbrains.com/products?code=IC
|
|
||||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
|
||||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
|
||||||
ideaVersion=2024.1.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-37
|
version=chylex-30
|
||||||
javaVersion=17
|
javaVersion=17
|
||||||
remoteRobotVersion=0.11.22
|
remoteRobotVersion=0.11.22
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
|
||||||
# [VERSION UPDATE] 2024.2 - remove when IdeaVim targets 2024.2
|
kotlin.incremental.useClasspathSnapshot=false
|
||||||
# Running IdeaVim in split mode requires 242. Update this version once 242 has been released, and remove it completely
|
|
||||||
# when IdeaVim targets 242, at which point runIdeSplitMode will run correctly with the target version.
|
|
||||||
# See also runIdeSplitMode
|
|
||||||
splitModeVersion=242-EAP-SNAPSHOT
|
|
||||||
|
|
||||||
|
|
||||||
# Please don't forget to update kotlin version in buildscript section
|
# Please don't forget to update kotlin version in buildscript section
|
||||||
# Also update kotlinxSerializationVersion version
|
# Also update kotlinxSerializationVersion version
|
||||||
@@ -48,10 +36,10 @@ youtrackToken=
|
|||||||
|
|
||||||
# Gradle settings
|
# Gradle settings
|
||||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||||
org.gradle.caching=true
|
|
||||||
|
|
||||||
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
|
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
|
||||||
kotlin.stdlib.default.dependency=false
|
kotlin.stdlib.default.dependency=false
|
||||||
|
|
||||||
# Disable incremental annotation processing
|
# Disable incremental annotation processing
|
||||||
ksp.incremental=false
|
ksp.incremental=false
|
||||||
|
|
||||||
|
@@ -20,17 +20,17 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core:2.3.12")
|
implementation("io.ktor:ktor-client-core:2.3.9")
|
||||||
implementation("io.ktor:ktor-client-cio:2.3.10")
|
implementation("io.ktor:ktor-client-cio:2.3.9")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
implementation("io.ktor:ktor-client-content-negotiation:2.3.9")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9")
|
||||||
implementation("io.ktor:ktor-client-auth:2.3.12")
|
implementation("io.ktor:ktor-client-auth:2.3.9")
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||||
|
|
||||||
// This is needed for jgit to connect to ssh
|
// This is needed for jgit to connect to ssh
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||||
implementation("com.vdurmont:semver4j:3.1.0")
|
implementation("com.vdurmont:semver4j:3.1.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +58,13 @@ 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")
|
||||||
@@ -107,13 +114,6 @@ tasks.register("calculateNewEapVersion", JavaExec::class) {
|
|||||||
args = listOf("${rootProject.rootDir}")
|
args = listOf("${rootProject.rootDir}")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("calculateNewEapVersionFromBranch", JavaExec::class) {
|
|
||||||
group = "release"
|
|
||||||
mainClass.set("scripts.release.CalculateNewEapVersionFromBranchKt")
|
|
||||||
classpath = sourceSets["main"].runtimeClasspath
|
|
||||||
args = listOf("${rootProject.rootDir}")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("calculateNewDevVersion", JavaExec::class) {
|
tasks.register("calculateNewDevVersion", JavaExec::class) {
|
||||||
group = "release"
|
group = "release"
|
||||||
mainClass.set("scripts.release.CalculateNewDevVersionKt")
|
mainClass.set("scripts.release.CalculateNewDevVersionKt")
|
||||||
|
@@ -49,13 +49,17 @@ suspend fun main() {
|
|||||||
}
|
}
|
||||||
val output = response.body<List<String>>().toSet()
|
val output = response.body<List<String>>().toSet()
|
||||||
println(output)
|
println(output)
|
||||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
if (knownPlugins != output) {
|
||||||
if (newPlugins.isNotEmpty()) {
|
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||||
// val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||||
error(
|
error(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Unregistered plugins:
|
Unregistered plugins:
|
||||||
${newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" }}
|
${if (newPlugins.isNotEmpty()) newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No unregistered plugins"}
|
||||||
|
|
||||||
|
Removed plugins:
|
||||||
|
${if (removedPlugins.isNotEmpty()) removedPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No removed plugins"}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -1,49 +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.release
|
|
||||||
|
|
||||||
fun main(args: Array<String> ) {
|
|
||||||
val projectDir = args[0]
|
|
||||||
println("Working directory: $projectDir")
|
|
||||||
val branch = withRepo(projectDir) { it.branch }
|
|
||||||
val (majorBranchVersion, minorBranchVersion) = versions(branch)
|
|
||||||
val versions = getVersionsExistingVersionsFor(majorBranchVersion, minorBranchVersion, projectDir)
|
|
||||||
val maxExistingVersion = versions.keys.maxOrNull()
|
|
||||||
|
|
||||||
val nextVersion = if (maxExistingVersion != null) {
|
|
||||||
if (maxExistingVersion.suffixTokens.isEmpty()) {
|
|
||||||
maxExistingVersion.nextPatch().withSuffix("eap.1").value
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
check(maxExistingVersion.suffixTokens.size == 2) {
|
|
||||||
"We should have exactly two suffix tokens. Current tokens: ${maxExistingVersion.suffixTokens.toList()}"
|
|
||||||
}
|
|
||||||
check(maxExistingVersion.suffixTokens[0] == "eap") {
|
|
||||||
"First suffix token must be eap. Current tokens: ${maxExistingVersion.suffixTokens.toList()}"
|
|
||||||
}
|
|
||||||
|
|
||||||
val newEapNumber = maxExistingVersion.suffixTokens[1].toInt().inc()
|
|
||||||
maxExistingVersion.withSuffix("eap.$newEapNumber").value
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"$majorBranchVersion.$minorBranchVersion.0-eap.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
println("Next eap version: $nextVersion")
|
|
||||||
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
|
|
||||||
}
|
|
||||||
|
|
||||||
private val regex = "releases/(\\d+)\\.(\\d+)\\.x".toRegex()
|
|
||||||
private fun versions(branchName: String): Pair<Int, Int> {
|
|
||||||
val match = regex.matchEntire(branchName) ?: error("Cannot match branch: $branchName")
|
|
||||||
val major = match.groups[1]
|
|
||||||
val minor = match.groups[2]
|
|
||||||
return major!!.value.toInt() to minor!!.value.toInt()
|
|
||||||
}
|
|
@@ -64,31 +64,6 @@ enum class ReleaseType {
|
|||||||
STABLE_NO_PATCH, // Version that ends on 0. Like 2.5.0
|
STABLE_NO_PATCH, // Version that ends on 0. Like 2.5.0
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getVersionsExistingVersionsFor(
|
|
||||||
majorVersion: Int,
|
|
||||||
minorVersion: Int,
|
|
||||||
projectDir: String,
|
|
||||||
): Map<Semver, ObjectId> {
|
|
||||||
val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build()
|
|
||||||
val git = Git(repository)
|
|
||||||
println(git.log().call().first())
|
|
||||||
println(git.tagList().call().first())
|
|
||||||
|
|
||||||
return git.tagList().call().mapNotNull { ref ->
|
|
||||||
runCatching {
|
|
||||||
// Git has two types of tags: light and annotated. This code detect hash of the commit for both types of tags
|
|
||||||
val revWalk = RevWalk(repository)
|
|
||||||
val tag = revWalk.parseAny(ref.objectId)
|
|
||||||
val commitHash = revWalk.peel(tag).id
|
|
||||||
val semver = Semver(ref.name.removePrefix("refs/tags/"))
|
|
||||||
if (semver.major == majorVersion && semver.minor == minorVersion) {
|
|
||||||
semver to commitHash
|
|
||||||
} else null
|
|
||||||
}.getOrNull()
|
|
||||||
}
|
|
||||||
.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getVersion(projectDir: String, releaseType: ReleaseType): Pair<Semver, ObjectId> {
|
internal fun getVersion(projectDir: String, releaseType: ReleaseType): Pair<Semver, ObjectId> {
|
||||||
val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build()
|
val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build()
|
||||||
val git = Git(repository)
|
val git = Git(repository)
|
||||||
|
62
scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
Normal file
62
scripts/src/main/kotlin/scripts/youTrackUsersAffected.kt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package scripts
|
||||||
|
|
||||||
|
import io.ktor.client.call.*
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
|
|
||||||
|
val areaWeights = setOf(
|
||||||
|
Triple("118-53212", "Plugins", 50),
|
||||||
|
Triple("118-53220", "Vim Script", 30),
|
||||||
|
Triple("118-54084", "Esc", 100),
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun updateRates() {
|
||||||
|
println("Updating rates of the issues")
|
||||||
|
areaWeights.forEach { (id, name, weight) ->
|
||||||
|
val unmappedIssues = unmappedIssues(name)
|
||||||
|
println("Got ${unmappedIssues.size} for $name area")
|
||||||
|
|
||||||
|
unmappedIssues.forEach { issueId ->
|
||||||
|
print("Trying to update issue $issueId: ")
|
||||||
|
val response = updateCustomField(issueId) {
|
||||||
|
put("name", "Affected Rate")
|
||||||
|
put("\$type", "SimpleIssueCustomField")
|
||||||
|
put("value", weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun unmappedIssues(area: String): List<String> {
|
||||||
|
val areaProcessed = if (" " in area) "{$area}" else area
|
||||||
|
val res = issuesQuery(
|
||||||
|
query = "project: VIM Affected Rate: {No affected rate} Area: $areaProcessed #Unresolved",
|
||||||
|
fields = "id,idReadable"
|
||||||
|
)
|
||||||
|
return res.body<JsonArray>().map { it.jsonObject }.map { it["idReadable"]!!.jsonPrimitive.content }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getAreasWithoutWeight(): Set<Pair<String, String>> {
|
||||||
|
val allAreas = getAreaValues()
|
||||||
|
return allAreas
|
||||||
|
.filterNot { it.key in areaWeights.map { it.first }.toSet() }
|
||||||
|
.entries
|
||||||
|
.map { it.key to it.value }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun main() {
|
||||||
|
updateRates()
|
||||||
|
}
|
20
settings.gradle
Normal file
20
settings.gradle
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Set repository for snapshot versions of gradle plugin
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = 'IdeaVIM'
|
||||||
|
include 'vim-engine'
|
||||||
|
include 'scripts'
|
||||||
|
include 'annotation-processors'
|
||||||
|
include 'tests:java-tests'
|
||||||
|
include 'tests:property-tests'
|
||||||
|
include 'tests:long-running-tests'
|
||||||
|
include 'tests:ui-ij-tests'
|
||||||
|
include 'tests:ui-py-tests'
|
||||||
|
include 'tests:ui-fixtures'
|
@@ -1,21 +0,0 @@
|
|||||||
// Set repository for snapshot versions of gradle plugin
|
|
||||||
pluginManagement {
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
|
||||||
}
|
|
||||||
gradlePluginPortal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rootProject.name = "IdeaVIM"
|
|
||||||
|
|
||||||
include("vim-engine")
|
|
||||||
include("scripts")
|
|
||||||
include("annotation-processors")
|
|
||||||
include("tests:java-tests")
|
|
||||||
include("tests:property-tests")
|
|
||||||
include("tests:long-running-tests")
|
|
||||||
include("tests:ui-ij-tests")
|
|
||||||
include("tests:ui-py-tests")
|
|
||||||
include("tests:ui-fixtures")
|
|
@@ -101,14 +101,10 @@ command:
|
|||||||
(WS | COLON)* range? (WS | COLON)* EXECUTE WS* (expr WS*)* (NEW_LINE | BAR)+
|
(WS | COLON)* range? (WS | COLON)* EXECUTE WS* (expr WS*)* (NEW_LINE | BAR)+
|
||||||
#ExecuteCommand|
|
#ExecuteCommand|
|
||||||
|
|
||||||
(WS | COLON)* range? (WS | COLON)* lShift
|
(WS | COLON)* range? (WS | COLON)* lShift (WS* commandArgument = ~(NEW_LINE | BAR)+)? ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
|
||||||
WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR))
|
|
||||||
(NEW_LINE | BAR)*
|
|
||||||
#ShiftLeftCommand|
|
#ShiftLeftCommand|
|
||||||
|
|
||||||
(WS | COLON)* range? (WS | COLON)* rShift
|
(WS | COLON)* range? (WS | COLON)* rShift (WS* commandArgument = ~(NEW_LINE | BAR)+)? ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
|
||||||
WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR))
|
|
||||||
(NEW_LINE | BAR)*
|
|
||||||
#ShiftRightCommand|
|
#ShiftRightCommand|
|
||||||
|
|
||||||
(WS | COLON)* range? (WS | COLON)*
|
(WS | COLON)* range? (WS | COLON)*
|
||||||
@@ -119,7 +115,6 @@ command:
|
|||||||
| PROMPT_REPLACE | P_LOWERCASE | P_UPPERCASE | PRINT | PREVIOUS_TAB | N_UPPERCASE | PREVIOUS_FILE | PLUG
|
| PROMPT_REPLACE | P_LOWERCASE | P_UPPERCASE | PRINT | PREVIOUS_TAB | N_UPPERCASE | PREVIOUS_FILE | PLUG
|
||||||
| ONLY | NO_HL_SEARCH | NEXT_TAB | N_LOWERCASE | NEXT_FILE | M_LOWERCASE | MOVE_TEXT | MARKS | K_LOWERCASE
|
| ONLY | NO_HL_SEARCH | NEXT_TAB | N_LOWERCASE | NEXT_FILE | M_LOWERCASE | MOVE_TEXT | MARKS | K_LOWERCASE
|
||||||
| MARK_COMMAND | JUMPS | J_LOWERCASE | JOIN_LINES | HISTORY | GO_TO_CHAR | SYMBOL | FIND | CLASS | F_LOWERCASE
|
| MARK_COMMAND | JUMPS | J_LOWERCASE | JOIN_LINES | HISTORY | GO_TO_CHAR | SYMBOL | FIND | CLASS | F_LOWERCASE
|
||||||
| CLEARJUMPS
|
|
||||||
| FILE | EXIT | E_LOWERCASE | EDIT_FILE | DUMP_LINE | DIGRAPH | DEL_MARKS | D_LOWERCASE | DEL_LINES | DELCMD
|
| FILE | EXIT | E_LOWERCASE | EDIT_FILE | DUMP_LINE | DIGRAPH | DEL_MARKS | D_LOWERCASE | DEL_LINES | DELCMD
|
||||||
| T_LOWERCASE | COPY | CMD_CLEAR | BUFFER_LIST | BUFFER_CLOSE | B_LOWERCASE | BUFFER | ASCII
|
| T_LOWERCASE | COPY | CMD_CLEAR | BUFFER_LIST | BUFFER_CLOSE | B_LOWERCASE | BUFFER | ASCII
|
||||||
| ACTIONLIST | ACTION | LOCKVAR | UNLOCKVAR | PACKADD | TABMOVE
|
| ACTIONLIST | ACTION | LOCKVAR | UNLOCKVAR | PACKADD | TABMOVE
|
||||||
@@ -619,7 +614,6 @@ BUFFER_CLOSE: 'bd' | 'bde' | 'bdel' | 'bdele' | 'bdelet' | 'bdelete';
|
|||||||
BUFFER_LIST: 'buffers' | 'ls' | 'files';
|
BUFFER_LIST: 'buffers' | 'ls' | 'files';
|
||||||
CALL: 'cal' | 'call';
|
CALL: 'cal' | 'call';
|
||||||
CLASS: 'cla' | 'clas' | 'class';
|
CLASS: 'cla' | 'clas' | 'class';
|
||||||
CLEARJUMPS: 'cle' | 'clea' | 'clear' | 'clearj' | 'clearju' | 'clearjum' | 'clearjump' | 'clearjumps';
|
|
||||||
CMD: 'com' | 'comm' | 'comma' | 'comman' | 'command';
|
CMD: 'com' | 'comm' | 'comma' | 'comman' | 'command';
|
||||||
CMD_CLEAR: 'comc' | 'comcl' | 'comcle' | 'comclea' | 'comclear';
|
CMD_CLEAR: 'comc' | 'comcl' | 'comcle' | 'comclea' | 'comclear';
|
||||||
COPY: 'co' | 'cop' | 'copy';
|
COPY: 'co' | 'cop' | 'copy';
|
@@ -14,36 +14,36 @@ import com.maddyhome.idea.vim.key.MappingOwner
|
|||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
object RegisterActions {
|
public object RegisterActions {
|
||||||
/**
|
/**
|
||||||
* Register all the key/action mappings for the plugin.
|
* Register all the key/action mappings for the plugin.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun registerActions() {
|
public fun registerActions() {
|
||||||
registerVimCommandActions()
|
registerVimCommandActions()
|
||||||
registerShortcutsWithoutActions()
|
registerShortcutsWithoutActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findAction(id: String): EditorActionHandlerBase? {
|
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||||
val commandBean = IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||||
?: EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||||
return commandBean.instance
|
return commandBean.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findActionOrDie(id: String): EditorActionHandlerBase {
|
public fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||||
return findAction(id) ?: throw RuntimeException("Action $id is not registered")
|
return findAction(id) ?: throw RuntimeException("Action $id is not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun unregisterActions() {
|
public fun unregisterActions() {
|
||||||
val keyGroup = VimPlugin.getKeyIfCreated()
|
val keyGroup = VimPlugin.getKeyIfCreated()
|
||||||
keyGroup?.unregisterCommandActions()
|
keyGroup?.unregisterCommandActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerVimCommandActions() {
|
private fun registerVimCommandActions() {
|
||||||
val parser = VimPlugin.getKey()
|
val parser = VimPlugin.getKey()
|
||||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
|
||||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||||
|
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerShortcutsWithoutActions() {
|
private fun registerShortcutsWithoutActions() {
|
||||||
|
@@ -37,8 +37,8 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
|
|||||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
|
||||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||||
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
||||||
import com.maddyhome.idea.vim.yank.YankGroupBase;
|
import com.maddyhome.idea.vim.yank.YankGroupBase;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
@@ -46,7 +46,6 @@ import org.jetbrains.annotations.Nls;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
|
||||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||||
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
|
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
|
||||||
@@ -124,12 +123,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
return (FileGroup)VimInjectorKt.getInjector().getFile();
|
return (FileGroup)VimInjectorKt.getInjector().getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull IjVimSearchGroup getSearch() {
|
public static @NotNull SearchGroup getSearch() {
|
||||||
return ApplicationManager.getApplication().getService(IjVimSearchGroup.class);
|
return ApplicationManager.getApplication().getService(SearchGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable IjVimSearchGroup getSearchIfCreated() {
|
public static @Nullable SearchGroup getSearchIfCreated() {
|
||||||
return ApplicationManager.getApplication().getServiceIfCreated(IjVimSearchGroup.class);
|
return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ProcessGroup getProcess() {
|
public static @NotNull ProcessGroup getProcess() {
|
||||||
@@ -283,13 +282,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
ideavimrcRegistered = true;
|
ideavimrcRegistered = true;
|
||||||
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
try {
|
executeIdeaVimRc(editor);
|
||||||
injector.getOptionGroup().startInitVimRc();
|
|
||||||
executeIdeaVimRc(editor);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
injector.getOptionGroup().endInitVimRc();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,14 +339,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void turnOffPlugin(boolean unsubscribe) {
|
private void turnOffPlugin(boolean unsubscribe) {
|
||||||
IjVimSearchGroup searchGroup = getSearchIfCreated();
|
SearchGroup searchGroup = getSearchIfCreated();
|
||||||
if (searchGroup != null) {
|
if (searchGroup != null) {
|
||||||
searchGroup.turnOff();
|
searchGroup.turnOff();
|
||||||
}
|
}
|
||||||
if (unsubscribe) {
|
if (unsubscribe) {
|
||||||
VimListenerManager.INSTANCE.turnOff();
|
VimListenerManager.INSTANCE.turnOff();
|
||||||
}
|
}
|
||||||
injector.getCommandLine().fullReset();
|
ExEntryPanel.fullReset();
|
||||||
|
|
||||||
// Unregister vim actions in command mode
|
// Unregister vim actions in command mode
|
||||||
RegisterActions.unregisterActions();
|
RegisterActions.unregisterActions();
|
||||||
|
@@ -12,13 +12,13 @@ import com.intellij.openapi.Disposable
|
|||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||||
|
|
||||||
@Service(Service.Level.PROJECT)
|
@Service(Service.Level.PROJECT)
|
||||||
internal class VimProjectService(val project: Project) : Disposable {
|
internal class VimProjectService(val project: Project) : Disposable {
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
// Not sure if this is a best solution
|
// Not sure if this is a best solution
|
||||||
ExEntryPanel.getInstance().editor = null
|
EditorHolderService.getInstance().editor = null
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@@ -32,7 +32,7 @@ import javax.swing.KeyStroke
|
|||||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||||
* way to get ideavim keys for this plugin. See VIM-3085
|
* way to get ideavim keys for this plugin. See VIM-3085
|
||||||
*/
|
*/
|
||||||
class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
||||||
private val handler = KeyHandler.getInstance()
|
private val handler = KeyHandler.getInstance()
|
||||||
private val traceTime = injector.globalOptions().ideatracetime
|
private val traceTime = injector.globalOptions().ideatracetime
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
|
|||||||
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, context.vim, handler.keyHandlerState)
|
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, 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")
|
||||||
|
@@ -1,32 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.action.ex
|
package com.maddyhome.idea.vim.action
|
||||||
|
|
||||||
import com.intellij.vim.annotations.CommandOrMotion
|
import com.intellij.vim.annotations.CommandOrMotion
|
||||||
import com.intellij.vim.annotations.Mode
|
import com.intellij.vim.annotations.Mode
|
||||||
|
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
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@CommandOrMotion(keys = [":"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
|
@CommandOrMotion(keys = [":"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
|
||||||
internal class ExEntryAction : VimActionHandler.SingleExecution() {
|
internal class ExEntryAction : VimActionHandler.SingleExecution() {
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_START_EX)
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
if (editor.isOneLineMode()) return false
|
VimPlugin.getProcess().startExCommand(editor, context, cmd)
|
||||||
injector.processGroup.startExEntry(editor, context, cmd)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -8,6 +8,6 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.action
|
package com.maddyhome.idea.vim.action
|
||||||
|
|
||||||
object IntellijCommandProvider : CommandProvider {
|
public object IntellijCommandProvider : CommandProvider {
|
||||||
override val commandListFileName: String = "intellij_commands.json"
|
override val commandListFileName: String = "intellij_commands.json"
|
||||||
}
|
}
|
@@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.maddyhome.idea.vim.action
|
|
||||||
|
|
||||||
import com.intellij.vim.annotations.CommandOrMotion
|
|
||||||
import com.intellij.vim.annotations.Mode
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear and redraw the screen
|
|
||||||
*
|
|
||||||
* Typically, an IDE does not need to redraw the screen - it's editor handles scrolling, etc. However, it can be
|
|
||||||
* necessary to clear the status line.
|
|
||||||
*/
|
|
||||||
@CommandOrMotion(keys = ["<C-L>"], modes = [Mode.NORMAL])
|
|
||||||
internal class RedrawAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
cmd: Command,
|
|
||||||
operatorArguments: OperatorArguments,
|
|
||||||
): Boolean {
|
|
||||||
injector.redrawService.redraw()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@@ -44,8 +44,6 @@ import com.maddyhome.idea.vim.listener.AceJumpService
|
|||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
import java.awt.event.InputEvent
|
import java.awt.event.InputEvent
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
@@ -60,7 +58,7 @@ import javax.swing.KeyStroke
|
|||||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||||
* way to get ideavim keys for this plugin. See VIM-3085
|
* way to get ideavim keys for this plugin. See VIM-3085
|
||||||
*/
|
*/
|
||||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||||
private val traceTime: Boolean
|
private val traceTime: Boolean
|
||||||
get() {
|
get() {
|
||||||
// Make sure the injector is initialized
|
// Make sure the injector is initialized
|
||||||
@@ -81,7 +79,12 @@ 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(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
keyHandler.handleKey(
|
||||||
|
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")
|
||||||
@@ -254,14 +257,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getEditor(e: AnActionEvent): Editor? {
|
private fun getEditor(e: AnActionEvent): Editor? = e.getData(PlatformDataKeys.EDITOR)
|
||||||
return e.getData(PlatformDataKeys.EDITOR)
|
|
||||||
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
|
|
||||||
ExEntryPanel.getInstance().editor
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Every time the key pressed with an active lookup, there is a decision:
|
* Every time the key pressed with an active lookup, there is a decision:
|
||||||
@@ -385,10 +381,6 @@ 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>()
|
||||||
|
|
||||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
|
|||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.helper.inRepeatMode
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||||
@@ -102,7 +102,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
val argument = cmd.argument ?: return false
|
val argument = cmd.argument ?: return false
|
||||||
if (!editor.inRepeatMode) {
|
if (!editor.vimStateMachine.isDotRepeatInProgress) {
|
||||||
argumentCaptured = argument
|
argumentCaptured = argument
|
||||||
}
|
}
|
||||||
val range = getMotionRange(editor, context, argument, operatorArguments)
|
val range = getMotionRange(editor, context, argument, operatorArguments)
|
||||||
|
@@ -17,6 +17,7 @@ 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.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
|
||||||
@@ -24,7 +25,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
|||||||
override val type: Command.Type = Command.Type.OTHER_WRITABLE
|
override val type: Command.Type = Command.Type.OTHER_WRITABLE
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
val state = injector.vimState
|
val state = editor.vimStateMachine
|
||||||
val lastCommand = VimRepeater.lastChangeCommand
|
val lastCommand = VimRepeater.lastChangeCommand
|
||||||
|
|
||||||
if (lastCommand == null && Extension.lastExtensionHandler == null) return false
|
if (lastCommand == null && Extension.lastExtensionHandler == null) return false
|
||||||
@@ -57,11 +58,11 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
|||||||
)
|
)
|
||||||
} else if (!repeatHandler && lastCommand != null) {
|
} else if (!repeatHandler && lastCommand != null) {
|
||||||
if (cmd.rawCount > 0) {
|
if (cmd.rawCount > 0) {
|
||||||
lastCommand.rawCount = cmd.count
|
lastCommand.count = cmd.count
|
||||||
val arg = lastCommand.argument
|
val arg = lastCommand.argument
|
||||||
if (arg != null) {
|
if (arg != null) {
|
||||||
val mot = arg.motion
|
val mot = arg.motion
|
||||||
mot.rawCount = 0
|
mot.count = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.executingCommand = lastCommand
|
state.executingCommand = lastCommand
|
||||||
|
@@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|||||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
|
||||||
class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
public class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
override val type: Command.Type = Command.Type.DELETE
|
||||||
override fun runAsMulticaret(
|
override fun runAsMulticaret(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
|
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|||||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
|
||||||
class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
override val type: Command.Type = Command.Type.DELETE
|
||||||
|
|
||||||
override fun execute(
|
override fun execute(
|
||||||
@@ -34,7 +34,7 @@ class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution()
|
|||||||
}
|
}
|
||||||
injector.editorGroup.notifyIdeaJoin(editor)
|
injector.editorGroup.notifyIdeaJoin(editor)
|
||||||
var res = true
|
var res = true
|
||||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||||
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
||||||
res = false
|
res = false
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
|
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
|
||||||
class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
override val type: Command.Type = Command.Type.DELETE
|
||||||
|
|
||||||
override fun executeForAllCarets(
|
override fun executeForAllCarets(
|
||||||
@@ -39,7 +39,7 @@ class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var res = true
|
var res = true
|
||||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset.point }.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(
|
||||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
|
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
|
||||||
class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
override val type: Command.Type = Command.Type.DELETE
|
||||||
|
|
||||||
override fun executeForAllCarets(
|
override fun executeForAllCarets(
|
||||||
@@ -39,7 +39,7 @@ class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExec
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var res = true
|
var res = true
|
||||||
editor.carets().sortedByDescending { it.offset }.forEach { caret ->
|
editor.carets().sortedByDescending { it.offset.point }.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(
|
||||||
|
@@ -36,18 +36,6 @@ internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELET
|
|||||||
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
|
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
|
||||||
override val type: Command.Type = Command.Type.MOTION
|
override val type: Command.Type = Command.Type.MOTION
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
cmd: Command,
|
|
||||||
operatorArguments: OperatorArguments
|
|
||||||
): Boolean {
|
|
||||||
val undo = injector.undo
|
|
||||||
val nanoTime = System.nanoTime()
|
|
||||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
|
||||||
return super.execute(editor, context, cmd, operatorArguments)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
|
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
|
||||||
@@ -60,18 +48,6 @@ internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
|
|||||||
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
|
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
|
||||||
override val type: Command.Type = Command.Type.MOTION
|
override val type: Command.Type = Command.Type.MOTION
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||||
|
|
||||||
override fun execute(
|
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
cmd: Command,
|
|
||||||
operatorArguments: OperatorArguments
|
|
||||||
): Boolean {
|
|
||||||
val undo = injector.undo
|
|
||||||
val nanoTime = System.nanoTime()
|
|
||||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
|
||||||
return super.execute(editor, context, cmd, operatorArguments)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
|
||||||
|
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
package com.maddyhome.idea.vim.action.ex
|
||||||
|
|
||||||
|
import com.intellij.vim.annotations.CommandOrMotion
|
||||||
|
import com.intellij.vim.annotations.Mode
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.command.Command
|
||||||
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by KeyHandler to process the contents of the ex entry panel
|
||||||
|
*
|
||||||
|
* The mapping for this action means that the ex command is executed as a write action
|
||||||
|
*/
|
||||||
|
@CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE])
|
||||||
|
public class ProcessExEntryAction : VimActionHandler.SingleExecution() {
|
||||||
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
|
override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX)
|
||||||
|
|
||||||
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
|
return VimPlugin.getProcess().processExEntry(editor, context)
|
||||||
|
}
|
||||||
|
}
|
72
src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
Normal file
72
src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.command
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||||
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPATIBILITY-LAYER: Additional class
|
||||||
|
* Please see: https://jb.gg/zo8n0r
|
||||||
|
*/
|
||||||
|
public class CommandState(private val machine: VimStateMachine) {
|
||||||
|
|
||||||
|
public val isOperatorPending: Boolean
|
||||||
|
get() = machine.isOperatorPending(machine.mode)
|
||||||
|
|
||||||
|
public val mode: Mode
|
||||||
|
get() {
|
||||||
|
val myMode = machine.mode
|
||||||
|
return when (myMode) {
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
|
||||||
|
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
|
||||||
|
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public val commandBuilder: CommandBuilder
|
||||||
|
get() = machine.commandBuilder
|
||||||
|
|
||||||
|
public val mappingState: MappingState
|
||||||
|
get() = machine.mappingState
|
||||||
|
|
||||||
|
public enum class Mode {
|
||||||
|
// Basic modes
|
||||||
|
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
|
||||||
|
|
||||||
|
// Additional modes
|
||||||
|
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum class SubMode {
|
||||||
|
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
@JvmStatic
|
||||||
|
public fun getInstance(editor: Editor): CommandState {
|
||||||
|
return CommandState(editor.vim.vimStateMachine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val CommandState.SubMode.engine: SelectionType
|
||||||
|
get() = when (this) {
|
||||||
|
CommandState.SubMode.NONE -> error("Unexpected value")
|
||||||
|
CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
|
||||||
|
CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
|
||||||
|
CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
|
||||||
|
}
|
@@ -12,18 +12,18 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.editor.LogicalPosition
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
|
||||||
class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
public class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
||||||
fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
public fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
||||||
|
|
||||||
companion object {
|
public companion object {
|
||||||
fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
public fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
||||||
// logical position "expands" tabs
|
// logical position "expands" tabs
|
||||||
val logicalPosition = editor.offsetToLogicalPosition(offset)
|
val logicalPosition = editor.offsetToLogicalPosition(offset)
|
||||||
val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
|
val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
|
||||||
return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
|
return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun atCaret(editor: Editor): CharacterPosition {
|
public fun atCaret(editor: Editor): CharacterPosition {
|
||||||
return fromOffset(editor, editor.caretModel.offset)
|
return fromOffset(editor, editor.caretModel.offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
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
|
||||||
@@ -37,12 +39,13 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(editor: Editor): IndentConfig {
|
fun create(editor: Editor, context: DataContext): IndentConfig {
|
||||||
return create(editor, editor.project)
|
return create(editor, PlatformDataKeys.PROJECT.getData(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(editor: Editor, project: Project?): IndentConfig {
|
@JvmOverloads
|
||||||
|
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 {
|
||||||
|
@@ -9,93 +9,40 @@ package com.maddyhome.idea.vim.ex
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.api.VimOutputPanel
|
import com.maddyhome.idea.vim.api.VimExOutputPanel
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.helper.vimExOutput
|
import com.maddyhome.idea.vim.helper.vimExOutput
|
||||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
|
/**
|
||||||
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanel {
|
* @author vlan
|
||||||
private var isActiveInTestMode = false
|
*/
|
||||||
|
public class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
|
||||||
|
override var text: String? = null
|
||||||
|
private set
|
||||||
|
|
||||||
val editor get() = myEditor.get()
|
override fun output(text: String) {
|
||||||
|
|
||||||
val isActive: Boolean
|
|
||||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
|
|
||||||
} else {
|
|
||||||
isActiveInTestMode
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addText(text: String, isNewLine: Boolean) {
|
|
||||||
if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun show() {
|
|
||||||
if (editor == null) return
|
|
||||||
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
|
|
||||||
if (currentPanel != null && currentPanel != this) currentPanel.close()
|
|
||||||
|
|
||||||
editor!!.vimExOutput = this
|
|
||||||
val exOutputPanel = ExOutputPanel.getInstance(editor!!)
|
|
||||||
if (!exOutputPanel.myActive) {
|
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
isActiveInTestMode = true
|
|
||||||
} else {
|
|
||||||
exOutputPanel.activate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var text: String = ""
|
|
||||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
|
|
||||||
} else {
|
|
||||||
// ExOutputPanel always returns a non-null string
|
|
||||||
field
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
|
|
||||||
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
|
|
||||||
val newValue = value.removeSuffix("\n")
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
|
|
||||||
} else {
|
|
||||||
field = newValue
|
|
||||||
isActiveInTestMode = newValue.isNotEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun output(text: String) {
|
|
||||||
this.text = text
|
this.text = text
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
editor?.let { ExOutputPanel.getInstance(it).close() }
|
ExOutputPanel.getInstance(myEditor).setText(text)
|
||||||
}
|
|
||||||
else {
|
|
||||||
isActiveInTestMode = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
override fun clear() {
|
||||||
|
text = null
|
||||||
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
ExOutputPanel.getInstance(myEditor).deactivate(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getInstance(editor: Editor): ExOutputModel {
|
public fun getInstance(editor: Editor): ExOutputModel {
|
||||||
var model = editor.vimExOutput
|
var model = editor.vimExOutput
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
model = ExOutputModel(WeakReference(editor))
|
model = ExOutputModel(editor)
|
||||||
editor.vimExOutput = model
|
editor.vimExOutput = model
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun tryGetInstance(editor: Editor) = editor.vimExOutput
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,8 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
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.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
@@ -23,9 +23,10 @@ import com.maddyhome.idea.vim.command.MappingMode
|
|||||||
import com.maddyhome.idea.vim.common.CommandAlias
|
import com.maddyhome.idea.vim.common.CommandAlias
|
||||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.helper.CommandLineHelper
|
||||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||||
import com.maddyhome.idea.vim.helper.inRepeatMode
|
|
||||||
import com.maddyhome.idea.vim.helper.noneOfEnum
|
import com.maddyhome.idea.vim.helper.noneOfEnum
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.key.MappingOwner
|
import com.maddyhome.idea.vim.key.MappingOwner
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
@@ -50,13 +51,13 @@ import javax.swing.KeyStroke
|
|||||||
*
|
*
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
object VimExtensionFacade {
|
public object VimExtensionFacade {
|
||||||
|
|
||||||
private val LOG = logger<VimExtensionFacade>()
|
private val LOG = logger<VimExtensionFacade>()
|
||||||
|
|
||||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putExtensionHandlerMapping(
|
public fun putExtensionHandlerMapping(
|
||||||
modes: Set<MappingMode>,
|
modes: Set<MappingMode>,
|
||||||
fromKeys: List<KeyStroke>,
|
fromKeys: List<KeyStroke>,
|
||||||
pluginOwner: MappingOwner,
|
pluginOwner: MappingOwner,
|
||||||
@@ -67,15 +68,13 @@ object VimExtensionFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPATIBILITY-LAYER: Additional method
|
||||||
|
* Please see: https://jb.gg/zo8n0r
|
||||||
|
*/
|
||||||
|
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated(
|
public fun putExtensionHandlerMapping(
|
||||||
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
|
||||||
ReplaceWith(
|
|
||||||
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
|
||||||
"com.maddyhome.idea.vim.VimPlugin"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
fun putExtensionHandlerMapping(
|
|
||||||
modes: Set<MappingMode>,
|
modes: Set<MappingMode>,
|
||||||
fromKeys: List<KeyStroke>,
|
fromKeys: List<KeyStroke>,
|
||||||
pluginOwner: MappingOwner,
|
pluginOwner: MappingOwner,
|
||||||
@@ -87,7 +86,7 @@ object VimExtensionFacade {
|
|||||||
|
|
||||||
/** The 'map' command for mapping keys to other keys. */
|
/** The 'map' command for mapping keys to other keys. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putKeyMapping(
|
public fun putKeyMapping(
|
||||||
modes: Set<MappingMode>,
|
modes: Set<MappingMode>,
|
||||||
fromKeys: List<KeyStroke>,
|
fromKeys: List<KeyStroke>,
|
||||||
pluginOwner: MappingOwner,
|
pluginOwner: MappingOwner,
|
||||||
@@ -99,7 +98,7 @@ object VimExtensionFacade {
|
|||||||
|
|
||||||
/** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
|
/** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putKeyMappingIfMissing(
|
public fun putKeyMappingIfMissing(
|
||||||
modes: Set<MappingMode>,
|
modes: Set<MappingMode>,
|
||||||
fromKeys: List<KeyStroke>,
|
fromKeys: List<KeyStroke>,
|
||||||
pluginOwner: MappingOwner,
|
pluginOwner: MappingOwner,
|
||||||
@@ -113,7 +112,7 @@ object VimExtensionFacade {
|
|||||||
/**
|
/**
|
||||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||||
*/
|
*/
|
||||||
fun addCommand(
|
public fun addCommand(
|
||||||
name: String,
|
name: String,
|
||||||
handler: CommandAliasHandler,
|
handler: CommandAliasHandler,
|
||||||
) {
|
) {
|
||||||
@@ -124,7 +123,7 @@ object VimExtensionFacade {
|
|||||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun addCommand(
|
public fun addCommand(
|
||||||
name: String,
|
name: String,
|
||||||
minimumNumberOfArguments: Int,
|
minimumNumberOfArguments: Int,
|
||||||
maximumNumberOfArguments: Int,
|
maximumNumberOfArguments: Int,
|
||||||
@@ -142,16 +141,16 @@ object VimExtensionFacade {
|
|||||||
* leaves the editor in the insert mode if it's been activated.
|
* leaves the editor in the insert mode if it's been activated.
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||||
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
val context = injector.executionContextManager.onEditor(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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun inputKeyStroke(editor: Editor): KeyStroke {
|
public fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||||
if (editor.vim.inRepeatMode) {
|
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
|
||||||
val input = Extension.consumeKeystroke()
|
val input = Extension.consumeKeystroke()
|
||||||
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
||||||
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
|
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
|
||||||
@@ -182,43 +181,43 @@ 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
|
||||||
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
public fun inputString(editor: Editor, prompt: String, finishOn: Char?): String {
|
||||||
return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
return service<CommandLineHelper>().inputString(editor.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()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getRegister(register: Char): List<KeyStroke>? {
|
public fun getRegister(register: Char): List<KeyStroke>? {
|
||||||
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||||
val reg = injector.registerGroup.getRegister(register) ?: return null
|
val reg = caret.registerStorage.getRegister(register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current contents of the given register */
|
/** Set the current contents of the given register */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
public fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
|
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current contents of the given register */
|
/** Set the current contents of the given register */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
public fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||||
injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current contents of the given register */
|
/** Set the current contents of the given register */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun exportScriptFunction(
|
public fun exportScriptFunction(
|
||||||
scope: Scope?,
|
scope: Scope?,
|
||||||
name: String,
|
name: String,
|
||||||
args: List<String>,
|
args: List<String>,
|
||||||
@@ -254,7 +253,7 @@ object VimExtensionFacade {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||||
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
||||||
editor, context, args ->
|
editor, context, args ->
|
||||||
|
|
||||||
@@ -275,6 +274,6 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interface ScriptFunction {
|
public fun interface ScriptFunction {
|
||||||
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||||
}
|
}
|
@@ -19,12 +19,12 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
* COMPATIBILITY-LAYER: Created a class, renamed original class
|
* COMPATIBILITY-LAYER: Created a class, renamed original class
|
||||||
* Please see: https://jb.gg/zo8n0r
|
* Please see: https://jb.gg/zo8n0r
|
||||||
*/
|
*/
|
||||||
interface VimExtensionHandler : ExtensionHandler {
|
public interface VimExtensionHandler : ExtensionHandler {
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
execute(editor.ij, context.ij)
|
execute(editor.ij, context.ij)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun execute(editor: Editor, context: DataContext)
|
public fun execute(editor: Editor, context: DataContext)
|
||||||
|
|
||||||
abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
public abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
package com.maddyhome.idea.vim.extension.argtextobj;
|
package com.maddyhome.idea.vim.extension.argtextobj;
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Document;
|
import com.intellij.openapi.editor.Document;
|
||||||
import com.maddyhome.idea.vim.KeyHandler;
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.*;
|
||||||
@@ -24,7 +23,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
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.vimscript.model.datatypes.VimString;
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
|
||||||
import org.jetbrains.annotations.Nls;
|
import org.jetbrains.annotations.Nls;
|
||||||
@@ -245,18 +244,19 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
IjVimEditor vimEditor = (IjVimEditor) editor;
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||||
|
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||||
|
|
||||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||||
//noinspection DuplicatedCode
|
//noinspection DuplicatedCode
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||||
} else {
|
} else {
|
||||||
InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
|
InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
|
||||||
@@ -265,7 +265,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
|
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ import com.intellij.psi.PsiElement
|
|||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@@ -33,8 +32,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
|||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.ex.ranges.Range
|
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||||
import com.maddyhome.idea.vim.ex.ranges.toTextRange
|
|
||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||||
@@ -46,6 +44,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
@@ -64,7 +63,7 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
selectionType: SelectionType,
|
selectionType: SelectionType,
|
||||||
resetCaret: Boolean = true,
|
resetCaret: Boolean = true,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val mode = editor.mode
|
val mode = editor.vimStateMachine.mode
|
||||||
if (mode !is Mode.VISUAL) {
|
if (mode !is Mode.VISUAL) {
|
||||||
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
||||||
}
|
}
|
||||||
@@ -183,10 +182,10 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
override val isRepeatable = true
|
override val isRepeatable = true
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
|
val commandState = editor.vimStateMachine
|
||||||
|
|
||||||
val keyState = KeyHandler.getInstance().keyHandlerState
|
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
|
||||||
keyState.commandBuilder.completeCommandPart(Argument(command))
|
commandState.commandBuilder.completeCommandPart(Argument(command))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,14 +248,8 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
* Used like `:1,3Commentary` or `g/fun/Commentary`
|
* Used like `:1,3Commentary` or `g/fun/Commentary`
|
||||||
*/
|
*/
|
||||||
private class CommentaryCommandAliasHandler : CommandAliasHandler {
|
private class CommentaryCommandAliasHandler : CommandAliasHandler {
|
||||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||||
Util.doCommentary(
|
Util.doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||||
editor,
|
|
||||||
context,
|
|
||||||
range.getLineRange(editor, editor.primaryCaret()).toTextRange(editor),
|
|
||||||
SelectionType.LINE_WISE,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ package com.maddyhome.idea.vim.extension.highlightedyank
|
|||||||
|
|
||||||
import com.intellij.ide.ui.LafManager
|
import com.intellij.ide.ui.LafManager
|
||||||
import com.intellij.ide.ui.LafManagerListener
|
import com.intellij.ide.ui.LafManagerListener
|
||||||
import com.intellij.openapi.application.ModalityState
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.colors.EditorColors
|
import com.intellij.openapi.editor.colors.EditorColors
|
||||||
import com.intellij.openapi.editor.markup.EffectType
|
import com.intellij.openapi.editor.markup.EffectType
|
||||||
@@ -19,24 +19,25 @@ import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
|||||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||||
import com.intellij.openapi.editor.markup.TextAttributes
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.util.Alarm
|
|
||||||
import com.intellij.util.Alarm.ThreadToUse
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.VimProjectService
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
|
import com.maddyhome.idea.vim.helper.StrictMode
|
||||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
||||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
import com.maddyhome.idea.vim.listener.VimInsertListener
|
||||||
import com.maddyhome.idea.vim.listener.VimYankListener
|
import com.maddyhome.idea.vim.listener.VimYankListener
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
internal const val DEFAULT_HIGHLIGHT_DURATION: Int = 300
|
internal const val DEFAULT_HIGHLIGHT_DURATION: Long = 300
|
||||||
|
|
||||||
@NonNls
|
@NonNls
|
||||||
private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_duration"
|
private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_duration"
|
||||||
@@ -77,128 +78,111 @@ internal class HighlightColorResetter : LafManagerListener {
|
|||||||
*/
|
*/
|
||||||
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
|
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
|
||||||
private val highlightHandler = HighlightHandler()
|
private val highlightHandler = HighlightHandler()
|
||||||
private var initialised = false
|
|
||||||
|
|
||||||
override fun getName() = "highlightedyank"
|
override fun getName() = "highlightedyank"
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
|
|
||||||
VimPlugin.getYank().addListener(this)
|
VimPlugin.getYank().addListener(this)
|
||||||
VimPlugin.getChange().addInsertListener(this)
|
VimPlugin.getChange().addInsertListener(this)
|
||||||
|
|
||||||
// Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
|
|
||||||
// IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
|
|
||||||
// _extension_ is disabled, make sure we're properly initialised each time we're called.
|
|
||||||
registerIdeaVimDisabledCallback()
|
|
||||||
initialised = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun registerIdeaVimDisabledCallback() {
|
|
||||||
// TODO: IdeaVim should help with lifecycle management here - VIM-3419
|
|
||||||
// IdeaVim should possibly unregister extensions, but it would also need to re-register them. We have to do this
|
|
||||||
// state management manually for now
|
|
||||||
Disposer.register(VimPlugin.getInstance().onOffDisposable) {
|
|
||||||
highlightHandler.clearYankHighlighters()
|
|
||||||
initialised = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
|
|
||||||
VimPlugin.getYank().removeListener(this)
|
VimPlugin.getYank().removeListener(this)
|
||||||
VimPlugin.getChange().removeInsertListener(this)
|
VimPlugin.getChange().removeInsertListener(this)
|
||||||
|
|
||||||
highlightHandler.clearYankHighlighters()
|
|
||||||
initialised = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun yankPerformed(editor: VimEditor, range: TextRange) {
|
override fun yankPerformed(editor: VimEditor, range: TextRange) {
|
||||||
ensureInitialised()
|
|
||||||
highlightHandler.highlightYankRange(editor.ij, range)
|
highlightHandler.highlightYankRange(editor.ij, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun insertModeStarted(editor: Editor) {
|
override fun insertModeStarted(editor: Editor) {
|
||||||
ensureInitialised()
|
highlightHandler.clearAllYankHighlighters()
|
||||||
highlightHandler.clearYankHighlighters()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ensureInitialised() {
|
|
||||||
if (!initialised) {
|
|
||||||
registerIdeaVimDisabledCallback()
|
|
||||||
initialised = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HighlightHandler {
|
private class HighlightHandler {
|
||||||
private val alarm = Alarm(ThreadToUse.SWING_THREAD)
|
private var editor: Editor? = null
|
||||||
private var lastEditor: Editor? = null
|
private val yankHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
||||||
private val highlighters = mutableSetOf<RangeHighlighter>()
|
|
||||||
|
|
||||||
fun highlightYankRange(editor: Editor, range: TextRange) {
|
fun highlightYankRange(editor: Editor, range: TextRange) {
|
||||||
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
|
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
|
||||||
clearYankHighlighters()
|
clearAllYankHighlighters()
|
||||||
|
|
||||||
lastEditor = editor
|
this.editor = editor
|
||||||
|
val project = editor.project
|
||||||
val attributes = getHighlightTextAttributes(editor)
|
if (project != null) {
|
||||||
for (i in 0 until range.size()) {
|
Disposer.register(
|
||||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
VimProjectService.getInstance(project),
|
||||||
range.startOffsets[i],
|
) {
|
||||||
range.endOffsets[i],
|
this.editor = null
|
||||||
HighlighterLayer.SELECTION,
|
yankHighlighters.clear()
|
||||||
attributes,
|
|
||||||
HighlighterTargetArea.EXACT_RANGE,
|
|
||||||
)
|
|
||||||
highlighters.add(highlighter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
|
|
||||||
val timeout = extractUsersHighlightDuration()
|
|
||||||
if (timeout >= 0) {
|
|
||||||
// Note modality. This is important when highlighting an editor when a modal dialog is open, such as the resolve
|
|
||||||
// conflict diff view
|
|
||||||
alarm.addRequest(
|
|
||||||
{ clearYankHighlighters() },
|
|
||||||
timeout,
|
|
||||||
ModalityState.any()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearYankHighlighters() {
|
|
||||||
alarm.cancelAllRequests()
|
|
||||||
// Make sure the last editor we saved is still alive before we use it. We can't just use the list of open editors
|
|
||||||
// because this list is empty when IdeaVim is disabled, so we're unable to clean up
|
|
||||||
lastEditor?.let { editor ->
|
|
||||||
if (!editor.isDisposed) {
|
|
||||||
highlighters.forEach { highlighter -> editor.markupModel.removeHighlighter(highlighter) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastEditor = null
|
|
||||||
highlighters.clear()
|
if (range.isMultiple) {
|
||||||
|
for (i in 0 until range.size()) {
|
||||||
|
highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
highlightSingleRange(editor, range.startOffset..range.endOffset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHighlightTextAttributes(editor: Editor) = TextAttributes(
|
fun clearAllYankHighlighters() {
|
||||||
|
yankHighlighters.forEach { highlighter ->
|
||||||
|
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||||
|
}
|
||||||
|
|
||||||
|
yankHighlighters.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
|
||||||
|
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||||
|
range.start,
|
||||||
|
range.endInclusive,
|
||||||
|
HighlighterLayer.SELECTION,
|
||||||
|
getHighlightTextAttributes(),
|
||||||
|
HighlighterTargetArea.EXACT_RANGE,
|
||||||
|
)
|
||||||
|
|
||||||
|
yankHighlighters.add(highlighter)
|
||||||
|
|
||||||
|
setClearHighlightRangeTimer(highlighter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
||||||
|
val timeout = extractUsersHighlightDuration()
|
||||||
|
|
||||||
|
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
|
||||||
|
if (timeout >= 0) {
|
||||||
|
Executors.newSingleThreadScheduledExecutor().schedule(
|
||||||
|
{
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
TimeUnit.MILLISECONDS,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getHighlightTextAttributes() = TextAttributes(
|
||||||
null,
|
null,
|
||||||
extractUsersHighlightColor(),
|
extractUsersHighlightColor(),
|
||||||
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
|
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
|
||||||
EffectType.SEARCH_MATCH,
|
EffectType.SEARCH_MATCH,
|
||||||
Font.PLAIN,
|
Font.PLAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun extractUsersHighlightDuration(): Int {
|
private fun extractUsersHighlightDuration(): Long {
|
||||||
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
|
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
|
||||||
// toVimNumber will return 0 for an invalid string. Let's force it to throw
|
it.toLong()
|
||||||
when (it) {
|
|
||||||
is VimString -> it.value.toInt()
|
|
||||||
else -> it.toVimNumber().value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractUsersHighlightColor(): Color {
|
private fun extractUsersHighlightColor(): Color {
|
||||||
return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
|
return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
|
||||||
val rgba = value.asString()
|
val rgba = value
|
||||||
.substring(4)
|
.substring(4)
|
||||||
.filter { it != '(' && it != ')' && !it.isWhitespace() }
|
.filter { it != '(' && it != ')' && !it.isWhitespace() }
|
||||||
.split(',')
|
.split(',')
|
||||||
@@ -208,11 +192,12 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
|
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: String) -> T): T {
|
||||||
val value = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
val value = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||||
if (value != null) {
|
|
||||||
|
if (value is VimString) {
|
||||||
return try {
|
return try {
|
||||||
extractFun(value)
|
extractFun(value.value)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@VimNlsSafe val message = MessageHelper.message(
|
@VimNlsSafe val message = MessageHelper.message(
|
||||||
"highlightedyank.invalid.value.of.0.1",
|
"highlightedyank.invalid.value.of.0.1",
|
||||||
|
@@ -14,7 +14,6 @@ import com.intellij.openapi.vfs.VirtualFile
|
|||||||
import com.intellij.psi.PsiComment
|
import com.intellij.psi.PsiComment
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
@@ -41,6 +40,7 @@ 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.PsiHelper
|
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
@@ -91,23 +91,22 @@ internal class Matchit : VimExtension {
|
|||||||
private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
|
private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val commandState = editor.vimStateMachine
|
||||||
val keyState = keyHandler.keyHandlerState
|
val count = commandState.commandBuilder.count
|
||||||
val count = keyState.commandBuilder.count
|
|
||||||
|
|
||||||
// Reset the command count so it doesn't transfer onto subsequent commands.
|
// Reset the command count so it doesn't transfer onto subsequent commands.
|
||||||
keyState.commandBuilder.resetCount()
|
editor.vimStateMachine.commandBuilder.resetCount()
|
||||||
|
|
||||||
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
||||||
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
||||||
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState)
|
val isInOpPending = commandState.isOperatorPending(editor.mode)
|
||||||
|
|
||||||
if (isInOpPending) {
|
if (isInOpPending) {
|
||||||
val matchitAction = MatchitAction()
|
val matchitAction = MatchitAction()
|
||||||
matchitAction.reverse = reverse
|
matchitAction.reverse = reverse
|
||||||
matchitAction.isInOpPending = true
|
matchitAction.isInOpPending = true
|
||||||
|
|
||||||
keyState.commandBuilder.completeCommandPart(
|
commandState.commandBuilder.completeCommandPart(
|
||||||
Argument(
|
Argument(
|
||||||
Command(
|
Command(
|
||||||
count,
|
count,
|
||||||
|
@@ -30,11 +30,11 @@ 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.group.visual.vimSetSelection
|
import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
|
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.endOffsetInclusive
|
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.findWordUnderCursor
|
|
||||||
import com.maddyhome.idea.vim.helper.inVisualMode
|
import com.maddyhome.idea.vim.helper.inVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
||||||
import com.maddyhome.idea.vim.helper.userData
|
import com.maddyhome.idea.vim.helper.userData
|
||||||
@@ -235,7 +235,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
|||||||
val text = if (editor.inVisualMode) {
|
val text = if (editor.inVisualMode) {
|
||||||
primaryCaret.selectedText ?: return
|
primaryCaret.selectedText ?: return
|
||||||
} else {
|
} else {
|
||||||
val range = findWordUnderCursor(editor, primaryCaret) ?: return
|
val range = SearchHelper.findWordUnderCursor(editor, primaryCaret) ?: return
|
||||||
if (range.startOffset > primaryCaret.offset) return
|
if (range.startOffset > primaryCaret.offset) return
|
||||||
IjVimEditor(editor).getText(range)
|
IjVimEditor(editor).getText(range)
|
||||||
}
|
}
|
||||||
@@ -300,7 +300,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
||||||
val range = findWordUnderCursor(editor, caret) ?: return null
|
val range = SearchHelper.findWordUnderCursor(editor, caret) ?: return null
|
||||||
if (range.startOffset > caret.offset) return null
|
if (range.startOffset > caret.offset) return null
|
||||||
|
|
||||||
enterVisualMode(editor.vim)
|
enterVisualMode(editor.vim)
|
||||||
@@ -327,6 +327,6 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
|||||||
|
|
||||||
private fun makePattern(text: String, whole: Boolean): String {
|
private fun makePattern(text: String, whole: Boolean): String {
|
||||||
// Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
|
// Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
|
||||||
return "\\V\\C" + if (whole) "\\<$text\\>" else text
|
return "\\V\\C" + SearchHelper.makeSearchPattern(text, whole)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ 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.common.CommandAlias
|
import com.maddyhome.idea.vim.common.CommandAlias
|
||||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||||
import com.maddyhome.idea.vim.ex.ranges.Range
|
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.group.KeyGroup
|
import com.maddyhome.idea.vim.group.KeyGroup
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
@@ -137,13 +137,13 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
|
class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
|
||||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||||
Util.callAction(editor, actionId, context)
|
Util.callAction(editor, actionId, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ToggleHandler : CommandAliasHandler {
|
class ToggleHandler : CommandAliasHandler {
|
||||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||||
val project = editor.ij.project ?: return
|
val project = editor.ij.project ?: return
|
||||||
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
||||||
if (toolWindow.isVisible) {
|
if (toolWindow.isVisible) {
|
||||||
@@ -155,7 +155,7 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CloseHandler : CommandAliasHandler {
|
class CloseHandler : CommandAliasHandler {
|
||||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||||
val project = editor.ij.project ?: return
|
val project = editor.ij.project ?: return
|
||||||
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
||||||
if (toolWindow.isVisible) {
|
if (toolWindow.isVisible) {
|
||||||
@@ -477,10 +477,6 @@ internal class NerdTree : VimExtension {
|
|||||||
"r",
|
"r",
|
||||||
NerdAction.ToIj("SynchronizeCurrentFile"),
|
NerdAction.ToIj("SynchronizeCurrentFile"),
|
||||||
)
|
)
|
||||||
registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
|
|
||||||
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
|
|
||||||
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
|
|
||||||
registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
|
|
||||||
registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
|
registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
|
||||||
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
||||||
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
||||||
@@ -506,6 +502,13 @@ internal class NerdTree : VimExtension {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for (c in ('a'..'z') + ('A'..'Z')) {
|
||||||
|
val ks = KeyStroke.getKeyStroke(c)
|
||||||
|
if (ks !in actionsRoot) {
|
||||||
|
registerCommand(c.toString(), NerdAction.Code { _, _, _ -> })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Util {
|
object Util {
|
||||||
|
@@ -8,9 +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.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
@@ -29,6 +27,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
@@ -65,7 +64,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, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||||
}
|
}
|
||||||
editor.exitVisualMode()
|
editor.exitVisualMode()
|
||||||
}
|
}
|
||||||
@@ -93,7 +92,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, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.sortedCarets().forEach { caret ->
|
editor.sortedCarets().forEach { caret ->
|
||||||
@@ -121,7 +120,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||||
)
|
)
|
||||||
// todo multicaret
|
// todo multicaret
|
||||||
doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
|
doReplace(ijEditor, editor.primaryCaret(), visualSelection)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +140,10 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
private fun doReplace(editor: Editor, 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 = registerGroup.getRegister(lastRegisterChar) ?: return
|
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||||
|
|
||||||
var usedType = savedRegister.type
|
var usedType = savedRegister.type
|
||||||
var usedText = savedRegister.text
|
var usedText = savedRegister.text
|
||||||
@@ -166,14 +165,13 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
|
|||||||
putToLine = -1,
|
putToLine = -1,
|
||||||
)
|
)
|
||||||
val vimEditor = editor.vim
|
val vimEditor = editor.vim
|
||||||
val keyHandler = KeyHandler.getInstance()
|
|
||||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||||
VimPlugin.getPut().putText(
|
VimPlugin.getPut().putText(
|
||||||
vimEditor,
|
vimEditor,
|
||||||
context.vim,
|
injector.executionContextManager.onEditor(editor.vim),
|
||||||
putData,
|
putData,
|
||||||
operatorArguments = OperatorArguments(
|
operatorArguments = OperatorArguments(
|
||||||
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
|
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||||
0,
|
0,
|
||||||
editor.vim.mode,
|
editor.vim.mode,
|
||||||
),
|
),
|
||||||
|
@@ -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
|
val position = caret.offset.point
|
||||||
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,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
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
|
||||||
@@ -35,7 +34,6 @@ 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
|
||||||
@@ -46,6 +44,7 @@ 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
|
||||||
@@ -101,7 +100,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, context.ij) ?: return
|
val pair = getOrInputPair(c, ijEditor) ?: return
|
||||||
|
|
||||||
editor.forEachCaret {
|
editor.forEachCaret {
|
||||||
val line = it.getBufferPosition().line
|
val line = it.getBufferPosition().line
|
||||||
@@ -151,7 +150,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, context.ij) ?: return
|
val newSurround = getOrInputPair(charTo, editor.ij) ?: return
|
||||||
runWriteAction { change(editor, context, charFrom, newSurround) }
|
runWriteAction { change(editor, context, charFrom, newSurround) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,12 +227,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' -> findBlockRange(editor, caret, '(', 1, true)
|
'(', ')', 'b' -> searchHelper.findBlockRange(editor, caret, '(', 1, true)
|
||||||
'[', ']' -> findBlockRange(editor, caret, '[', 1, true)
|
'[', ']' -> searchHelper.findBlockRange(editor, caret, '[', 1, true)
|
||||||
'{', '}', 'B' -> findBlockRange(editor, caret, '{', 1, true)
|
'{', '}', 'B' -> searchHelper.findBlockRange(editor, caret, '{', 1, true)
|
||||||
'<', '>' -> findBlockRange(editor, caret, '<', 1, true)
|
'<', '>' -> searchHelper.findBlockRange(editor, caret, '<', 1, true)
|
||||||
'`', '\'', '"' -> {
|
'`', '\'', '"' -> {
|
||||||
val caretOffset = caret.offset
|
val caretOffset = caret.offset.point
|
||||||
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)
|
||||||
@@ -270,23 +269,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 ijEditor = vimEditor.ij
|
val editor = vimEditor.ij
|
||||||
val c = getChar(ijEditor)
|
val c = getChar(editor)
|
||||||
if (c.code == 0) return true
|
if (c.code == 0) return true
|
||||||
|
|
||||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
val pair = getOrInputPair(c, editor) ?: return false
|
||||||
|
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val change = VimPlugin.getChange()
|
val change = VimPlugin.getChange()
|
||||||
if (supportsMultipleCursors) {
|
if (supportsMultipleCursors) {
|
||||||
ijEditor.runWithEveryCaretAndRestore {
|
editor.runWithEveryCaretAndRestore {
|
||||||
applyOnce(ijEditor, change, pair, count)
|
applyOnce(editor, change, pair, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
applyOnce(ijEditor, change, pair, count)
|
applyOnce(editor, change, pair, count)
|
||||||
// Jump back to start
|
// Jump back to start
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -348,8 +347,8 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
||||||
val tagInput = inputString(editor, context, "<", '>')
|
val tagInput = inputString(editor, "<", '>')
|
||||||
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)
|
||||||
@@ -362,18 +361,17 @@ private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, Str
|
|||||||
|
|
||||||
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, context, "function: ", null)
|
val functionNameInput = inputString(editor, "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, context: DataContext): Pair<String, String>? = when (c) {
|
private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = when (c) {
|
||||||
'<', 't' -> inputTagPair(editor, context)
|
'<', 't' -> inputTagPair(editor)
|
||||||
'f' -> inputFunctionName(editor, context, false)
|
'f' -> inputFunctionName(editor, false)
|
||||||
'F' -> inputFunctionName(editor, context, true)
|
'F' -> inputFunctionName(editor, true)
|
||||||
else -> getSurroundPair(c)
|
else -> getSurroundPair(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
package com.maddyhome.idea.vim.extension.textobjentire;
|
package com.maddyhome.idea.vim.extension.textobjentire;
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Caret;
|
import com.intellij.openapi.editor.Caret;
|
||||||
import com.maddyhome.idea.vim.KeyHandler;
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||||
import com.maddyhome.idea.vim.api.VimEditor;
|
import com.maddyhome.idea.vim.api.VimEditor;
|
||||||
@@ -24,7 +23,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -134,18 +133,17 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
|
||||||
|
|
||||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||||
//noinspection DuplicatedCode
|
//noinspection DuplicatedCode
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||||
} else {
|
} else {
|
||||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||||
@@ -155,7 +153,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
textObjectHandler, Command.Type.MOTION,
|
textObjectHandler, Command.Type.MOTION,
|
||||||
EnumSet.noneOf(CommandFlags.class))));
|
EnumSet.noneOf(CommandFlags.class))));
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
package com.maddyhome.idea.vim.extension.textobjindent;
|
package com.maddyhome.idea.vim.extension.textobjindent;
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Caret;
|
import com.intellij.openapi.editor.Caret;
|
||||||
import com.maddyhome.idea.vim.KeyHandler;
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||||
import com.maddyhome.idea.vim.api.VimEditor;
|
import com.maddyhome.idea.vim.api.VimEditor;
|
||||||
@@ -25,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
|||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -132,11 +131,10 @@ public class VimIndentObject implements VimExtension {
|
|||||||
// This is done as a separate step so that it works even when the caret is inside the indentation.
|
// This is done as a separate step so that it works even when the caret is inside the indentation.
|
||||||
int offset = caretLineStartOffset;
|
int offset = caretLineStartOffset;
|
||||||
int indentSize = 0;
|
int indentSize = 0;
|
||||||
while (offset < charSequence.length()) {
|
while (++offset < charSequence.length()) {
|
||||||
final char ch = charSequence.charAt(offset);
|
final char ch = charSequence.charAt(offset);
|
||||||
if (ch == ' ' || ch == '\t') {
|
if (ch == ' ' || ch == '\t') {
|
||||||
++indentSize;
|
++indentSize;
|
||||||
++offset;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -264,18 +262,17 @@ public class VimIndentObject implements VimExtension {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
IjVimEditor vimEditor = (IjVimEditor)editor;
|
IjVimEditor vimEditor = (IjVimEditor)editor;
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
|
||||||
|
|
||||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||||
|
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||||
EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||||
} else {
|
} else {
|
||||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||||
@@ -285,7 +282,7 @@ public class VimIndentObject implements VimExtension {
|
|||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
textObjectHandler, Command.Type.MOTION,
|
textObjectHandler, Command.Type.MOTION,
|
||||||
EnumSet.noneOf(CommandFlags.class))));
|
EnumSet.noneOf(CommandFlags.class))));
|
||||||
}
|
}
|
||||||
|
@@ -57,9 +57,8 @@ import com.maddyhome.idea.vim.helper.CharacterHelper.changeCase
|
|||||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.NumberType
|
import com.maddyhome.idea.vim.helper.NumberType
|
||||||
|
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||||
import com.maddyhome.idea.vim.helper.findNumberUnderCursor
|
|
||||||
import com.maddyhome.idea.vim.helper.findNumbersInRange
|
|
||||||
import com.maddyhome.idea.vim.helper.inInsertMode
|
import com.maddyhome.idea.vim.helper.inInsertMode
|
||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||||
@@ -84,7 +83,7 @@ import kotlin.math.max
|
|||||||
/**
|
/**
|
||||||
* Provides all the insert/replace related functionality
|
* Provides all the insert/replace related functionality
|
||||||
*/
|
*/
|
||||||
class ChangeGroup : VimChangeGroupBase() {
|
public class ChangeGroup : VimChangeGroupBase() {
|
||||||
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
|
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
|
||||||
private val listener: EditorMouseListener = object : EditorMouseListener {
|
private val listener: EditorMouseListener = object : EditorMouseListener {
|
||||||
override fun mouseClicked(event: EditorMouseEvent) {
|
override fun mouseClicked(event: EditorMouseEvent) {
|
||||||
@@ -95,7 +94,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun editorCreated(editor: Editor?, disposable: Disposable) {
|
public fun editorCreated(editor: Editor?, disposable: Disposable) {
|
||||||
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
|
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,9 +102,6 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
val ijContext = context.ij
|
val ijContext = context.ij
|
||||||
val doc = vimEditor.editor.document
|
val doc = vimEditor.editor.document
|
||||||
val undo = injector.undo
|
|
||||||
val nanoTime = System.nanoTime()
|
|
||||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
|
||||||
CommandProcessor.getInstance().executeCommand(
|
CommandProcessor.getInstance().executeCommand(
|
||||||
editor.project, {
|
editor.project, {
|
||||||
ApplicationManager.getApplication()
|
ApplicationManager.getApplication()
|
||||||
@@ -201,7 +197,7 @@ 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, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
changeCase(editor, caret, caret.offset.point, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||||
motion = injector.motion.getHorizontalMotion(
|
motion = injector.motion.getHorizontalMotion(
|
||||||
editor,
|
editor,
|
||||||
caret,
|
caret,
|
||||||
@@ -239,7 +235,8 @@ 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 = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
|
val pad =
|
||||||
|
EditorHelper.pad((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context, line, column)
|
||||||
val offset = editor.getLineEndOffset(line)
|
val offset = editor.getLineEndOffset(line)
|
||||||
insertText(editor, caret, offset, pad)
|
insertText(editor, caret, offset, pad)
|
||||||
}
|
}
|
||||||
@@ -398,7 +395,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
) {
|
) {
|
||||||
val startPos = editor.offsetToBufferPosition(caret.offset)
|
val startPos = editor.offsetToBufferPosition(caret.offset.point)
|
||||||
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
|
||||||
@@ -454,7 +451,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
dir: Int,
|
dir: Int,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
) {
|
) {
|
||||||
val start = caret.offset
|
val start = caret.offset.point
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -488,7 +485,7 @@ 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)
|
val indentConfig = create((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context)
|
||||||
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)
|
||||||
@@ -571,7 +568,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val startLine = range.startLine
|
val startLine = range.startLine
|
||||||
val endLine = range.endLine
|
val endLine = range.endLine
|
||||||
val count = range.size
|
val count = endLine - startLine + 1
|
||||||
if (count < 2) {
|
if (count < 2) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -654,7 +651,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val alpha = nf.contains("alpha")
|
val alpha = nf.contains("alpha")
|
||||||
val hex = nf.contains("hex")
|
val hex = nf.contains("hex")
|
||||||
val octal = nf.contains("octal")
|
val octal = nf.contains("octal")
|
||||||
val numberRanges = findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
|
val numberRanges = SearchHelper.findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
|
||||||
val newNumbers: MutableList<String?> = ArrayList()
|
val newNumbers: MutableList<String?> = ArrayList()
|
||||||
for (i in numberRanges.indices) {
|
for (i in numberRanges.indices) {
|
||||||
val numberRange = numberRanges[i]
|
val numberRange = numberRanges[i]
|
||||||
@@ -677,7 +674,8 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val alpha = nf.contains("alpha")
|
val alpha = nf.contains("alpha")
|
||||||
val hex = nf.contains("hex")
|
val hex = nf.contains("hex")
|
||||||
val octal = nf.contains("octal")
|
val octal = nf.contains("octal")
|
||||||
val range = findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
|
val range =
|
||||||
|
SearchHelper.findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
logger.debug("no number on line")
|
logger.debug("no number on line")
|
||||||
return false
|
return false
|
||||||
@@ -786,11 +784,11 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
return number
|
return number
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addInsertListener(listener: VimInsertListener) {
|
public fun addInsertListener(listener: VimInsertListener) {
|
||||||
insertListeners.add(listener)
|
insertListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeInsertListener(listener: VimInsertListener) {
|
public fun removeInsertListener(listener: VimInsertListener) {
|
||||||
insertListeners.remove(listener)
|
insertListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,13 +20,11 @@ import com.intellij.openapi.components.Storage;
|
|||||||
import com.intellij.openapi.editor.*;
|
import com.intellij.openapi.editor.*;
|
||||||
import com.intellij.openapi.editor.event.CaretEvent;
|
import com.intellij.openapi.editor.event.CaretEvent;
|
||||||
import com.intellij.openapi.editor.event.CaretListener;
|
import com.intellij.openapi.editor.event.CaretListener;
|
||||||
import com.intellij.openapi.editor.ex.EditorEx;
|
|
||||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.maddyhome.idea.vim.KeyHandler;
|
import com.maddyhome.idea.vim.KeyHandler;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
|
||||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||||
@@ -40,14 +38,10 @@ import org.jetbrains.annotations.NonNls;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.intellij.openapi.editor.EditorSettings.*;
|
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
||||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
||||||
@@ -61,31 +55,12 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
|
|
||||||
private Boolean isKeyRepeat = null;
|
private Boolean isKeyRepeat = null;
|
||||||
|
|
||||||
// TODO: Get rid of this custom line converter once we support soft wraps properly
|
|
||||||
// The builtin relative line converter looks like it's using Vim's logical lines for counting, where a Vim logical
|
|
||||||
// line is a buffer line, or a single line representing a fold of several buffer lines. This converter is counting
|
|
||||||
// screen lines (but badly - if you're on the second line of a wrapped line, it still counts like you're on the first.
|
|
||||||
// We really want to use Vim logical lines, but we don't currently support them for movement - we move by screen line.
|
|
||||||
|
|
||||||
private final CaretListener myLineNumbersCaretListener = new CaretListener() {
|
private final CaretListener myLineNumbersCaretListener = new CaretListener() {
|
||||||
@Override
|
@Override
|
||||||
public void caretPositionChanged(@NotNull CaretEvent e) {
|
public void caretPositionChanged(@NotNull CaretEvent e) {
|
||||||
// We don't get notified when the IDE's settings change, so make sure we're up-to-date when the caret moves
|
final boolean requiresRepaint = e.getNewPosition().line != e.getOldPosition().line;
|
||||||
final Editor editor = e.getEditor();
|
if (requiresRepaint && options(injector, new IjVimEditor(e.getEditor())).getRelativenumber()) {
|
||||||
boolean relativenumber = ijOptions(injector, new IjVimEditor(editor)).getRelativenumber();
|
repaintRelativeLineNumbers(e.getEditor());
|
||||||
if (relativenumber) {
|
|
||||||
if (!hasRelativeLineNumbersInstalled(editor)) {
|
|
||||||
installRelativeLineNumbers(editor);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// We must repaint on each caret move, so we update when caret's visual line doesn't match logical line
|
|
||||||
repaintRelativeLineNumbers(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (hasRelativeLineNumbersInstalled(editor)) {
|
|
||||||
removeRelativeLineNumbers(editor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -98,10 +73,11 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
editor.getCaretModel().addCaretListener(myLineNumbersCaretListener);
|
editor.getCaretModel().addCaretListener(myLineNumbersCaretListener);
|
||||||
UserDataManager.setVimEditorGroup(editor, true);
|
UserDataManager.setVimEditorGroup(editor, true);
|
||||||
|
|
||||||
|
UserDataManager.setVimLineNumbersInitialState(editor, editor.getSettings().isLineNumbersShown());
|
||||||
updateLineNumbers(editor);
|
updateLineNumbers(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deinitLineNumbers(@NotNull Editor editor) {
|
private void deinitLineNumbers(@NotNull Editor editor, boolean isReleasing) {
|
||||||
if (isProjectDisposed(editor) || !supportsVimLineNumbers(editor) || !UserDataManager.getVimEditorGroup(editor)) {
|
if (isProjectDisposed(editor) || !supportsVimLineNumbers(editor) || !UserDataManager.getVimEditorGroup(editor)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -110,6 +86,14 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
UserDataManager.setVimEditorGroup(editor, false);
|
UserDataManager.setVimEditorGroup(editor, false);
|
||||||
|
|
||||||
removeRelativeLineNumbers(editor);
|
removeRelativeLineNumbers(editor);
|
||||||
|
|
||||||
|
// Don't reset the built in line numbers if we're releasing the editor. If we do, EditorSettings.setLineNumbersShown
|
||||||
|
// can cause the editor to refresh settings and can call into FileManagerImpl.getCachedPsiFile AFTER FileManagerImpl
|
||||||
|
// has been disposed (Closing the project with a Find Usages result showing a preview panel is a good repro case).
|
||||||
|
// See IDEA-184351 and VIM-1671
|
||||||
|
if (!isReleasing) {
|
||||||
|
setBuiltinLineNumbers(editor, UserDataManager.getVimLineNumbersInitialState(editor));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
|
private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
|
||||||
@@ -123,22 +107,41 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void updateLineNumbers(final @NotNull Editor editor) {
|
private static void updateLineNumbers(final @NotNull Editor editor) {
|
||||||
final boolean isLineNumbersShown = editor.getSettings().isLineNumbersShown();
|
final EffectiveOptions options = options(injector, new IjVimEditor(editor));
|
||||||
if (!isLineNumbersShown) {
|
final boolean relativeNumber = options.getRelativenumber();
|
||||||
return;
|
final boolean number = options.getNumber();
|
||||||
|
|
||||||
|
final boolean showBuiltinEditorLineNumbers = shouldShowBuiltinLineNumbers(editor, number, relativeNumber);
|
||||||
|
|
||||||
|
final EditorSettings settings = editor.getSettings();
|
||||||
|
if (settings.isLineNumbersShown() ^ showBuiltinEditorLineNumbers) {
|
||||||
|
// Update line numbers later since it may be called from a caret listener
|
||||||
|
// on the caret move and it may move the caret internally
|
||||||
|
ApplicationManager.getApplication().invokeLater(() -> {
|
||||||
|
if (editor.isDisposed()) return;
|
||||||
|
setBuiltinLineNumbers(editor, showBuiltinEditorLineNumbers);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final LineNumerationType lineNumerationType = editor.getSettings().getLineNumerationType();
|
if (relativeNumber) {
|
||||||
if (lineNumerationType == LineNumerationType.RELATIVE || lineNumerationType == LineNumerationType.HYBRID) {
|
|
||||||
if (!hasRelativeLineNumbersInstalled(editor)) {
|
if (!hasRelativeLineNumbersInstalled(editor)) {
|
||||||
installRelativeLineNumbers(editor);
|
installRelativeLineNumbers(editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (hasRelativeLineNumbersInstalled(editor)) {
|
||||||
removeRelativeLineNumbers(editor);
|
removeRelativeLineNumbers(editor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean shouldShowBuiltinLineNumbers(final @NotNull Editor editor, boolean number, boolean relativeNumber) {
|
||||||
|
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
|
||||||
|
return initialState || number || relativeNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setBuiltinLineNumbers(final @NotNull Editor editor, boolean show) {
|
||||||
|
editor.getSettings().setLineNumbersShown(show);
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
|
private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
|
||||||
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
|
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
|
||||||
}
|
}
|
||||||
@@ -213,22 +216,51 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
|
|
||||||
initLineNumbers(editor);
|
initLineNumbers(editor);
|
||||||
|
|
||||||
// Listen for changes to the font size, so we can hide the ex text field/output panel
|
// We add Vim bindings to all opened editors, even read-only editors. We also add bindings to editors that are used
|
||||||
if (editor instanceof EditorEx editorEx) {
|
// elsewhere in the IDE, rather than just for editing project files. This includes editors used as part of the UI,
|
||||||
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
// such as the VCS commit message, or used as read-only viewers for text output, such as log files in run
|
||||||
|
// configurations or the Git Console tab. And editors are used for interactive stdin/stdout for console-based run
|
||||||
|
// configurations.
|
||||||
|
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
|
||||||
|
// to INSERT mode for interactive editors. Recognising these can be a bit tricky.
|
||||||
|
// These additional interactive editors are not file-based, but must have a writable document. However, log output
|
||||||
|
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
|
||||||
|
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
|
||||||
|
// read-only and also hides the caret).
|
||||||
|
// Furthermore, the interactive stdin/stdout console output is hosted in a read-only editor, but it can still be
|
||||||
|
// edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's `isViewer` property and
|
||||||
|
// allows typing if the associated process (if any) is still running. We can get the editor's console view and check
|
||||||
|
// this ourselves, but we have to wait until the editor has finished initialising before it's available in user
|
||||||
|
// data.
|
||||||
|
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
|
||||||
|
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
|
||||||
|
Runnable switchToInsertMode = () -> {
|
||||||
|
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
||||||
|
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
|
||||||
|
KeyHandler.getInstance().reset(new IjVimEditor(editor));
|
||||||
|
};
|
||||||
|
if (!editor.isViewer() &&
|
||||||
|
!EditorHelper.isFileEditor(editor) &&
|
||||||
|
editor.getDocument().isWritable() &&
|
||||||
|
!CommandStateHelper.inInsertMode(editor)) {
|
||||||
|
switchToInsertMode.run();
|
||||||
}
|
}
|
||||||
|
ApplicationManager.getApplication().invokeLater(
|
||||||
|
() -> {
|
||||||
|
if (editor.isDisposed()) return;
|
||||||
|
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
|
||||||
|
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
|
||||||
|
switchToInsertMode.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void editorDeinit(@NotNull Editor editor) {
|
public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
|
||||||
deinitLineNumbers(editor);
|
deinitLineNumbers(editor, isReleased);
|
||||||
UserDataManager.unInitializeEditor(editor);
|
UserDataManager.unInitializeEditor(editor);
|
||||||
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
||||||
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
||||||
if (editor instanceof EditorEx editorEx) {
|
|
||||||
editorEx.removePropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
|
public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
|
||||||
@@ -290,18 +322,17 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
private static class RelativeLineNumberConverter implements LineNumberConverter {
|
private static class RelativeLineNumberConverter implements LineNumberConverter {
|
||||||
@Override
|
@Override
|
||||||
public Integer convert(@NotNull Editor editor, int lineNumber) {
|
public Integer convert(@NotNull Editor editor, int lineNumber) {
|
||||||
final IjVimEditor ijVimEditor = new IjVimEditor(editor);
|
final boolean number = options(injector, new IjVimEditor(editor)).getNumber();
|
||||||
final boolean number = options(injector, ijVimEditor).getNumber();
|
|
||||||
final int caretLine = editor.getCaretModel().getLogicalPosition().line;
|
final int caretLine = editor.getCaretModel().getLogicalPosition().line;
|
||||||
|
|
||||||
// lineNumber is 1 based
|
// lineNumber is 1 based
|
||||||
if ((lineNumber - 1) == caretLine) {
|
if (number && (lineNumber - 1) == caretLine) {
|
||||||
return number ? lineNumber : 0;
|
return lineNumber;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final int visualLine = ijVimEditor.bufferLineToVisualLine(lineNumber - 1);
|
final int visualLine = new IjVimEditor(editor).bufferLineToVisualLine(lineNumber - 1);
|
||||||
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
|
final int currentVisualLine = new IjVimEditor(editor).bufferLineToVisualLine(caretLine);
|
||||||
return Math.abs(caretVisualLine - visualLine);
|
return Math.abs(currentVisualLine - visualLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,51 +371,16 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
private Stream<Editor> getLocalEditors() {
|
private Stream<Editor> getLocalEditors() {
|
||||||
// Always fetch local editors. If we're hosting a Code With Me session, any connected guests will create hidden
|
// Always fetch local editors. If we're hosting a Code With Me session, any connected guests will create hidden
|
||||||
// editors to handle syntax highlighting, completion requests, etc. We need to make sure that IdeaVim only makes
|
// editors to handle syntax highlighting, completion requests, etc. We need to make sure that IdeaVim only makes
|
||||||
// changes (e.g., adding search highlights) to local editors so things don't incorrectly flow through to any Clients.
|
// changes (e.g. adding search highlights) to local editors, so things don't incorrectly flow through to any Clients.
|
||||||
// In non-CWM scenarios, or if IdeaVim is installed on the Client, there are only ever local editors, so this will
|
// In non-CWM scenarios, or if IdeaVim is installed on the Client, there are only ever local editors, so this will
|
||||||
// also work there. In Gateway remote development scenarios, IdeaVim should not be installed on the host, only the
|
// also work there. In Gateway remote development scenarios, IdeaVim should not be installed on the host, only the
|
||||||
// Client, so all should work there too.
|
// Client, so all should work there too.
|
||||||
// Note that most IdeaVim operations are in response to interactive keystrokes, which would mean that
|
// Note that most IdeaVim operations are in response to interactive keystrokes, which would mean that
|
||||||
// ClientEditorManager.getCurrentInstance would return local editors. However, some operations are in response to
|
// ClientEditorManager.getCurrentInstance would return local editors. However, some operations are in response to
|
||||||
// events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
|
// events such as document change (to update search highlights) and these can come from CWM guests, and we'd get the
|
||||||
// the remote editors.
|
// remote editors.
|
||||||
// This invocation will always get local editors, regardless of the current context.
|
// This invocation will always get local editors, regardless of current context.
|
||||||
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
|
final ClientAppSession localSession = ClientSessionsManager.getAppSessions(ClientKind.LOCAL).get(0);
|
||||||
if (!appSessions.isEmpty()) {
|
return localSession.getService(ClientEditorManager.class).editors();
|
||||||
ClientAppSession localSession = appSessions.get(0);
|
|
||||||
return localSession.getService(ClientEditorManager.class).editors();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Stream.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens to property changes from the editor to hide ex text field/output panel when the editor's font is zoomed
|
|
||||||
*/
|
|
||||||
private static class FontSizeChangeListener implements PropertyChangeListener {
|
|
||||||
public static FontSizeChangeListener INSTANCE = new FontSizeChangeListener();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
if (VimPlugin.isNotEnabled()) return;
|
|
||||||
if (evt.getPropertyName().equals(EditorEx.PROP_FONT_SIZE)) {
|
|
||||||
Object source = evt.getSource();
|
|
||||||
if (source instanceof Editor editor) {
|
|
||||||
// The editor is being zoomed, so hide the command line or output panel, if they're being shown. On the one
|
|
||||||
// hand, it's a little rude to cancel a command line for the user, but on the other, the panels obscure the
|
|
||||||
// zoom indicator, so it looks nicer if we hide them.
|
|
||||||
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
|
|
||||||
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
|
|
||||||
if (activeCommandLine != null) {
|
|
||||||
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), true, false);
|
|
||||||
}
|
|
||||||
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
|
|
||||||
if (exOutputModel != null) {
|
|
||||||
exOutputModel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.group
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.Service
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
|
||||||
|
@Service
|
||||||
|
internal class EditorHolderService {
|
||||||
|
var editor: Editor? = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstance(): EditorHolderService = service()
|
||||||
|
}
|
||||||
|
}
|
448
src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
Normal file
448
src/main/java/com/maddyhome/idea/vim/group/FileGroup.java
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.group;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext;
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||||
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
|
import com.intellij.openapi.editor.Document;
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.editor.LogicalPosition;
|
||||||
|
import com.intellij.openapi.fileEditor.*;
|
||||||
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||||
|
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||||
|
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
||||||
|
import com.intellij.openapi.fileTypes.FileType;
|
||||||
|
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager;
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import com.intellij.psi.search.FilenameIndex;
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope;
|
||||||
|
import com.intellij.psi.search.ProjectScope;
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
|
import com.maddyhome.idea.vim.api.*;
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorHelperRt;
|
||||||
|
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||||
|
import com.maddyhome.idea.vim.helper.SearchHelper;
|
||||||
|
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||||
|
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||||
|
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
|
||||||
|
|
||||||
|
public class FileGroup extends VimFileBase {
|
||||||
|
public boolean openFile(@NotNull String filename, @NotNull ExecutionContext context) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("openFile(" + filename + ")");
|
||||||
|
}
|
||||||
|
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext()); // API change - don't merge
|
||||||
|
if (project == null) return false;
|
||||||
|
|
||||||
|
VirtualFile found = findFile(filename, project);
|
||||||
|
|
||||||
|
if (found != null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("found file: " + found);
|
||||||
|
}
|
||||||
|
// Can't open a file unless it has a known file type. The next call will return the known type.
|
||||||
|
// If unknown, IDEA will prompt the user to pick a type.
|
||||||
|
FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project);
|
||||||
|
|
||||||
|
//noinspection IfStatementWithIdenticalBranches
|
||||||
|
if (type != null) {
|
||||||
|
FileEditorManager fem = FileEditorManager.getInstance(project);
|
||||||
|
fem.openFile(found, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// There was no type and user didn't pick one. Don't open the file
|
||||||
|
// Return true here because we found the file but the user canceled by not picking a type.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable VirtualFile findFile(@NotNull String filename, @NotNull Project project) {
|
||||||
|
VirtualFile found;
|
||||||
|
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
||||||
|
// it only supports forward slash on Unix (tested on Mac)
|
||||||
|
// VFS works with both directory separators (tested on Mac and Windows)
|
||||||
|
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||||
|
String relativePath = filename.substring(2);
|
||||||
|
String dir = System.getProperty("user.home");
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("home dir file");
|
||||||
|
logger.debug("looking for " + relativePath + " in " + dir);
|
||||||
|
}
|
||||||
|
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, relativePath));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
|
||||||
|
|
||||||
|
if (found == null) {
|
||||||
|
found = findByNameInContentRoots(filename, project);
|
||||||
|
if (found == null) {
|
||||||
|
found = findByNameInProject(filename, project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private VirtualFile findByNameInContentRoots(@NotNull String filename, @NotNull Project project) {
|
||||||
|
VirtualFile found = null;
|
||||||
|
ProjectRootManager prm = ProjectRootManager.getInstance(project);
|
||||||
|
VirtualFile[] roots = prm.getContentRoots();
|
||||||
|
for (int i = 0; i < roots.length; i++) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("root[" + i + "] = " + roots[i].getPath());
|
||||||
|
}
|
||||||
|
found = roots[i].findFileByRelativePath(filename);
|
||||||
|
if (found != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static VirtualFile findByNameInProject(@NotNull String filename, @NotNull Project project) {
|
||||||
|
GlobalSearchScope projectScope = ProjectScope.getProjectScope(project);
|
||||||
|
Collection<VirtualFile> names = FilenameIndex.getVirtualFilesByName(filename, projectScope);
|
||||||
|
if (!names.isEmpty()) {
|
||||||
|
return names.stream().findFirst().get();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the current editor.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void closeFile(@NotNull VimEditor editor, @NotNull ExecutionContext context) {
|
||||||
|
final Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||||
|
if (project != null) {
|
||||||
|
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||||
|
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||||
|
final VirtualFile virtualFile = fileEditorManager.getCurrentFile();
|
||||||
|
|
||||||
|
if (virtualFile != null && window != null) {
|
||||||
|
// During the work on VIM-2912 I've changed the close function to this one.
|
||||||
|
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
||||||
|
//window.getManager().closeFile(virtualFile, true, false);
|
||||||
|
window.closeFile(virtualFile);
|
||||||
|
|
||||||
|
// Get focus after closing tab
|
||||||
|
window.requestFocus(true);
|
||||||
|
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
|
// This thing doesn't have an implementation in test mode
|
||||||
|
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes editor.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void closeFile(int number, @NotNull ExecutionContext context) {
|
||||||
|
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||||
|
if (project == null) return;
|
||||||
|
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||||
|
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||||
|
VirtualFile[] editors = fileEditorManager.getOpenFiles();
|
||||||
|
if (window != null) {
|
||||||
|
if (number >= 0 && number < editors.length) {
|
||||||
|
fileEditorManager.closeFile(editors[number], window);
|
||||||
|
}
|
||||||
|
} if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
|
// This thing doesn't have an implementation in test mode
|
||||||
|
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves specific file in the project.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void saveFile(@NotNull ExecutionContext context) {
|
||||||
|
NativeAction action;
|
||||||
|
if (globalIjOptions(injector).getIdeawrite().contains(IjOptionConstants.ideawrite_all)) {
|
||||||
|
action = injector.getNativeActionManager().getSaveAll();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
action = injector.getNativeActionManager().getSaveCurrent();
|
||||||
|
}
|
||||||
|
ExecuteExtensionKt.execute(action, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves all files in the project.
|
||||||
|
*/
|
||||||
|
public void saveFiles(@NotNull ExecutionContext context) {
|
||||||
|
ExecuteExtensionKt.execute(VimInjectorKt.getInjector().getNativeActionManager().getSaveAll(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects then next or previous editor.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean selectFile(int count, @NotNull ExecutionContext context) {
|
||||||
|
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||||
|
if (project == null) return false;
|
||||||
|
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||||
|
VirtualFile[] editors = fem.getOpenFiles();
|
||||||
|
if (count == 99) {
|
||||||
|
count = editors.length - 1;
|
||||||
|
}
|
||||||
|
if (count < 0 || count >= editors.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fem.openFile(editors[count], true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects then next or previous editor.
|
||||||
|
*/
|
||||||
|
public void selectNextFile(int count, @NotNull ExecutionContext context) {
|
||||||
|
Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||||
|
if (project == null) return;
|
||||||
|
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||||
|
VirtualFile[] editors = fem.getOpenFiles();
|
||||||
|
VirtualFile current = fem.getSelectedFiles()[0];
|
||||||
|
for (int i = 0; i < editors.length; i++) {
|
||||||
|
if (editors[i].equals(current)) {
|
||||||
|
int pos = (i + (count % editors.length) + editors.length) % editors.length;
|
||||||
|
|
||||||
|
fem.openFile(editors[pos], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects previous editor tab.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void selectPreviousTab(@NotNull ExecutionContext context) {
|
||||||
|
Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||||
|
if (project == null) return;
|
||||||
|
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||||
|
if (vf != null && vf.isValid()) {
|
||||||
|
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VimPlugin.indicateError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previous tab.
|
||||||
|
*/
|
||||||
|
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
|
||||||
|
Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||||
|
if (project == null) return null;
|
||||||
|
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||||
|
if (vf != null && vf.isValid()) {
|
||||||
|
return vf;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable Editor selectEditor(Project project, @NotNull VirtualFile file) {
|
||||||
|
FileEditorManager fMgr = FileEditorManager.getInstance(project);
|
||||||
|
FileEditor[] feditors = fMgr.openFile(file, true);
|
||||||
|
if (feditors.length > 0) {
|
||||||
|
if (feditors[0] instanceof TextEditor) {
|
||||||
|
Editor editor = ((TextEditor)feditors[0]).getEditor();
|
||||||
|
if (!editor.isDisposed()) {
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayLocationInfo(@NotNull VimEditor vimEditor) {
|
||||||
|
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
Document doc = editor.getDocument();
|
||||||
|
|
||||||
|
if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
|
||||||
|
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||||
|
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
|
||||||
|
int endoff = doc.getLineEndOffset(lp.line);
|
||||||
|
if (endoff < EditorHelperRt.getFileSize(editor) && doc.getCharsSequence().charAt(endoff) == '\n') {
|
||||||
|
endoff--;
|
||||||
|
}
|
||||||
|
int ecol = endoff - doc.getLineStartOffset(lp.line);
|
||||||
|
LogicalPosition elp = editor.offsetToLogicalPosition(endoff);
|
||||||
|
|
||||||
|
msg.append("Col ").append(col + 1);
|
||||||
|
if (col != lp.column) {
|
||||||
|
msg.append("-").append(lp.column + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.append(" of ").append(ecol + 1);
|
||||||
|
if (ecol != elp.column) {
|
||||||
|
msg.append("-").append(elp.column + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||||
|
int total = new IjVimEditor(editor).lineCount();
|
||||||
|
|
||||||
|
msg.append("; Line ").append(lline + 1).append(" of ").append(total);
|
||||||
|
|
||||||
|
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||||
|
|
||||||
|
msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
|
||||||
|
|
||||||
|
int offset = editor.getCaretModel().getOffset();
|
||||||
|
int size = EditorHelperRt.getFileSize(editor);
|
||||||
|
|
||||||
|
msg.append("; Character ").append(offset + 1).append(" of ").append(size);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.append("Selected ");
|
||||||
|
|
||||||
|
TextRange vr = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
|
||||||
|
editor.getSelectionModel().getBlockSelectionEnds());
|
||||||
|
vr.normalize();
|
||||||
|
|
||||||
|
int lines;
|
||||||
|
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||||
|
int words = cp.getCount();
|
||||||
|
int word = 0;
|
||||||
|
if (vr.isMultiple()) {
|
||||||
|
lines = vr.size();
|
||||||
|
int cols = vr.getMaxLength();
|
||||||
|
|
||||||
|
msg.append(cols).append(" Cols; ");
|
||||||
|
|
||||||
|
for (int i = 0; i < vr.size(); i++) {
|
||||||
|
cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
|
||||||
|
word += cp.getCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogicalPosition slp = editor.offsetToLogicalPosition(vr.getStartOffset());
|
||||||
|
LogicalPosition elp = editor.offsetToLogicalPosition(vr.getEndOffset());
|
||||||
|
|
||||||
|
lines = elp.line - slp.line + 1;
|
||||||
|
|
||||||
|
cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
|
||||||
|
word = cp.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
int total = new IjVimEditor(editor).lineCount();
|
||||||
|
|
||||||
|
msg.append(lines).append(" of ").append(total).append(" Lines");
|
||||||
|
|
||||||
|
msg.append("; ").append(word).append(" of ").append(words).append(" Words");
|
||||||
|
|
||||||
|
int chars = vr.getSelectionCount();
|
||||||
|
int size = EditorHelperRt.getFileSize(editor);
|
||||||
|
|
||||||
|
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
VimPlugin.showMessage(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayFileInfo(@NotNull VimEditor vimEditor, boolean fullPath) {
|
||||||
|
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
VirtualFile vf = EditorHelper.getVirtualFile(editor);
|
||||||
|
if (vf != null) {
|
||||||
|
msg.append('"');
|
||||||
|
if (fullPath) {
|
||||||
|
msg.append(vf.getPath());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Project project = editor.getProject();
|
||||||
|
if (project != null) {
|
||||||
|
VirtualFile root = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(vf);
|
||||||
|
if (root != null) {
|
||||||
|
msg.append(vf.getPath().substring(root.getPath().length() + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.append(vf.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.append("\" ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.append("\"[No File]\" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Document doc = editor.getDocument();
|
||||||
|
if (!doc.isWritable()) {
|
||||||
|
msg.append("[RO] ");
|
||||||
|
}
|
||||||
|
else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
||||||
|
msg.append("[+] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||||
|
int total = new IjVimEditor(editor).lineCount();
|
||||||
|
int pct = (int)((float)lline / (float)total * 100f + 0.5);
|
||||||
|
|
||||||
|
msg.append("line ").append(lline + 1).append(" of ").append(total);
|
||||||
|
msg.append(" --").append(pct).append("%-- ");
|
||||||
|
|
||||||
|
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||||
|
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lline);
|
||||||
|
|
||||||
|
msg.append("col ").append(col + 1);
|
||||||
|
if (col != lp.column) {
|
||||||
|
msg.append("-").append(lp.column + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
VimPlugin.showMessage(msg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Respond to editor tab selection and remember the last used tab
|
||||||
|
*/
|
||||||
|
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
|
||||||
|
if (event.getOldFile() != null) {
|
||||||
|
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,444 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2023 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
package com.maddyhome.idea.vim.group
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
|
||||||
import com.intellij.openapi.fileEditor.TextEditor
|
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
|
||||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters
|
|
||||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.project.ProjectManager
|
|
||||||
import com.intellij.openapi.roots.ProjectRootManager
|
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager
|
|
||||||
import com.intellij.psi.search.FilenameIndex
|
|
||||||
import com.intellij.psi.search.ProjectScope
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
|
||||||
import com.maddyhome.idea.vim.api.VimFileBase
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
|
||||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
|
||||||
import com.maddyhome.idea.vim.helper.countWords
|
|
||||||
import com.maddyhome.idea.vim.helper.fileSize
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
|
||||||
import com.maddyhome.idea.vim.newapi.execute
|
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class FileGroup : VimFileBase() {
|
|
||||||
override fun openFile(filename: String, context: ExecutionContext): Boolean {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("openFile($filename)")
|
|
||||||
}
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context)
|
|
||||||
?: return false // API change - don't merge
|
|
||||||
|
|
||||||
val found = findFile(filename, project)
|
|
||||||
|
|
||||||
if (found != null) {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("found file: $found")
|
|
||||||
}
|
|
||||||
// Can't open a file unless it has a known file type. The next call will return the known type.
|
|
||||||
// If unknown, IDEA will prompt the user to pick a type.
|
|
||||||
val type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project)
|
|
||||||
|
|
||||||
if (type != null) {
|
|
||||||
val fem = FileEditorManager.getInstance(project)
|
|
||||||
fem.openFile(found, true)
|
|
||||||
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// There was no type and user didn't pick one. Don't open the file
|
|
||||||
// Return true here because we found the file but the user canceled by not picking a type.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
VimPlugin.showMessage(message("unable.to.find.0", filename))
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findFile(filename: String, project: Project): VirtualFile? {
|
|
||||||
var found: VirtualFile?
|
|
||||||
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
|
||||||
// it only supports forward slash on Unix (tested on Mac)
|
|
||||||
// VFS works with both directory separators (tested on Mac and Windows)
|
|
||||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
|
||||||
val relativePath = filename.substring(2)
|
|
||||||
val dir = System.getProperty("user.home")
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("home dir file")
|
|
||||||
logger.debug("looking for $relativePath in $dir")
|
|
||||||
}
|
|
||||||
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(dir, relativePath))
|
|
||||||
} else {
|
|
||||||
found = LocalFileSystem.getInstance().findFileByIoFile(File(filename))
|
|
||||||
|
|
||||||
if (found == null) {
|
|
||||||
found = findByNameInContentRoots(filename, project)
|
|
||||||
if (found == null) {
|
|
||||||
found = findByNameInProject(filename, project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findByNameInContentRoots(filename: String, project: Project): VirtualFile? {
|
|
||||||
var found: VirtualFile? = null
|
|
||||||
val prm = ProjectRootManager.getInstance(project)
|
|
||||||
val roots = prm.contentRoots
|
|
||||||
for (i in roots.indices) {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("root[" + i + "] = " + roots[i].path)
|
|
||||||
}
|
|
||||||
found = roots[i].findFileByRelativePath(filename)
|
|
||||||
if (found != null) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the current editor.
|
|
||||||
*/
|
|
||||||
override fun closeFile(editor: VimEditor, context: ExecutionContext) {
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext))
|
|
||||||
if (project != null) {
|
|
||||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
|
||||||
val window = fileEditorManager.currentWindow
|
|
||||||
val virtualFile = fileEditorManager.currentFile
|
|
||||||
|
|
||||||
if (virtualFile != null && window != null) {
|
|
||||||
// During the work on VIM-2912 I've changed the close function to this one.
|
|
||||||
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
|
||||||
//window.getManager().closeFile(virtualFile, true, false);
|
|
||||||
window.closeFile(virtualFile)
|
|
||||||
|
|
||||||
// Get focus after closing tab
|
|
||||||
window.requestFocus(true)
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
// This thing doesn't have an implementation in test mode
|
|
||||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes editor.
|
|
||||||
*/
|
|
||||||
override fun closeFile(number: Int, context: ExecutionContext) {
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
|
||||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
|
||||||
val window = fileEditorManager.currentWindow
|
|
||||||
val editors = fileEditorManager.openFiles
|
|
||||||
if (window != null) {
|
|
||||||
if (number >= 0 && number < editors.size) {
|
|
||||||
fileEditorManager.closeFile(editors[number], window)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
// This thing doesn't have an implementation in test mode
|
|
||||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves specific file in the project.
|
|
||||||
*/
|
|
||||||
override fun saveFile(context: ExecutionContext) {
|
|
||||||
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
|
|
||||||
injector.nativeActionManager.saveAll
|
|
||||||
} else {
|
|
||||||
injector.nativeActionManager.saveCurrent
|
|
||||||
}
|
|
||||||
action.execute(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves all files in the project.
|
|
||||||
*/
|
|
||||||
override fun saveFiles(context: ExecutionContext) {
|
|
||||||
injector.nativeActionManager.saveAll.execute(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects then next or previous editor.
|
|
||||||
*/
|
|
||||||
override fun selectFile(count: Int, context: ExecutionContext): Boolean {
|
|
||||||
var count = count
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return false
|
|
||||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
|
||||||
val editors = fem.openFiles
|
|
||||||
if (count == 99) {
|
|
||||||
count = editors.size - 1
|
|
||||||
}
|
|
||||||
if (count < 0 || count >= editors.size) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fem.openFile(editors[count], true)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects then next or previous editor.
|
|
||||||
*/
|
|
||||||
override fun selectNextFile(count: Int, context: ExecutionContext) {
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
|
||||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
|
||||||
val editors = fem.openFiles
|
|
||||||
val current = fem.selectedFiles[0]
|
|
||||||
for (i in editors.indices) {
|
|
||||||
if (editors[i] == current) {
|
|
||||||
val pos = (i + (count % editors.size) + editors.size) % editors.size
|
|
||||||
|
|
||||||
fem.openFile(editors[pos], true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects previous editor tab.
|
|
||||||
*/
|
|
||||||
override fun selectPreviousTab(context: ExecutionContext) {
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext)) ?: return
|
|
||||||
val vf = getInstance(project).lastTab
|
|
||||||
if (vf != null && vf.isValid) {
|
|
||||||
FileEditorManager.getInstance(project).openFile(vf, true)
|
|
||||||
} else {
|
|
||||||
VimPlugin.indicateError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the previous tab.
|
|
||||||
*/
|
|
||||||
fun getPreviousTab(context: DataContext): VirtualFile? {
|
|
||||||
val project = PlatformDataKeys.PROJECT.getData(context) ?: return null
|
|
||||||
val vf = getInstance(project).lastTab
|
|
||||||
if (vf != null && vf.isValid) {
|
|
||||||
return vf
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun selectEditor(project: Project, file: VirtualFile): Editor? {
|
|
||||||
val fMgr = FileEditorManager.getInstance(project)
|
|
||||||
val feditors = fMgr.openFile(file, true)
|
|
||||||
if (feditors.size > 0) {
|
|
||||||
if (feditors[0] is TextEditor) {
|
|
||||||
val editor = (feditors[0] as TextEditor).editor
|
|
||||||
if (!editor.isDisposed) {
|
|
||||||
return editor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun displayLocationInfo(vimEditor: VimEditor) {
|
|
||||||
val editor = (vimEditor as IjVimEditor).editor
|
|
||||||
val msg = StringBuilder()
|
|
||||||
val doc = editor.document
|
|
||||||
|
|
||||||
if (injector.vimState.mode !is VISUAL) {
|
|
||||||
val lp = editor.caretModel.logicalPosition
|
|
||||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
|
||||||
var endoff = doc.getLineEndOffset(lp.line)
|
|
||||||
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
|
|
||||||
endoff--
|
|
||||||
}
|
|
||||||
val ecol = endoff - doc.getLineStartOffset(lp.line)
|
|
||||||
val elp = editor.offsetToLogicalPosition(endoff)
|
|
||||||
|
|
||||||
msg.append("Col ").append(col + 1)
|
|
||||||
if (col != lp.column) {
|
|
||||||
msg.append("-").append(lp.column + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.append(" of ").append(ecol + 1)
|
|
||||||
if (ecol != elp.column) {
|
|
||||||
msg.append("-").append(elp.column + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val lline = editor.caretModel.logicalPosition.line
|
|
||||||
val total = IjVimEditor(editor).lineCount()
|
|
||||||
|
|
||||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
|
|
||||||
|
|
||||||
val cp = countWords(vimEditor)
|
|
||||||
|
|
||||||
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
|
|
||||||
|
|
||||||
val offset = editor.caretModel.offset
|
|
||||||
val size = editor.fileSize
|
|
||||||
|
|
||||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
|
|
||||||
} else {
|
|
||||||
msg.append("Selected ")
|
|
||||||
|
|
||||||
val vr = TextRange(
|
|
||||||
editor.selectionModel.blockSelectionStarts,
|
|
||||||
editor.selectionModel.blockSelectionEnds
|
|
||||||
)
|
|
||||||
vr.normalize()
|
|
||||||
|
|
||||||
val lines: Int
|
|
||||||
var cp = countWords(vimEditor)
|
|
||||||
val words = cp.count
|
|
||||||
var word = 0
|
|
||||||
if (vr.isMultiple) {
|
|
||||||
lines = vr.size()
|
|
||||||
val cols = vr.maxLength
|
|
||||||
|
|
||||||
msg.append(cols).append(" Cols; ")
|
|
||||||
|
|
||||||
for (i in 0 until vr.size()) {
|
|
||||||
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
|
|
||||||
word += cp.count
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val slp = editor.offsetToLogicalPosition(vr.startOffset)
|
|
||||||
val elp = editor.offsetToLogicalPosition(vr.endOffset)
|
|
||||||
|
|
||||||
lines = elp.line - slp.line + 1
|
|
||||||
|
|
||||||
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
|
|
||||||
word = cp.count
|
|
||||||
}
|
|
||||||
|
|
||||||
val total = IjVimEditor(editor).lineCount()
|
|
||||||
|
|
||||||
msg.append(lines).append(" of ").append(total).append(" Lines")
|
|
||||||
|
|
||||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
|
|
||||||
|
|
||||||
val chars = vr.selectionCount
|
|
||||||
val size = editor.fileSize
|
|
||||||
|
|
||||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
VimPlugin.showMessage(msg.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
|
||||||
val editor = (vimEditor as IjVimEditor).editor
|
|
||||||
val msg = StringBuilder()
|
|
||||||
val vf = EditorHelper.getVirtualFile(editor)
|
|
||||||
if (vf != null) {
|
|
||||||
msg.append('"')
|
|
||||||
if (fullPath) {
|
|
||||||
msg.append(vf.path)
|
|
||||||
} else {
|
|
||||||
val project = editor.project
|
|
||||||
if (project != null) {
|
|
||||||
val root = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(vf)
|
|
||||||
if (root != null) {
|
|
||||||
msg.append(vf.path.substring(root.path.length + 1))
|
|
||||||
} else {
|
|
||||||
msg.append(vf.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
msg.append("\" ")
|
|
||||||
} else {
|
|
||||||
msg.append("\"[No File]\" ")
|
|
||||||
}
|
|
||||||
|
|
||||||
val doc = editor.document
|
|
||||||
if (!doc.isWritable) {
|
|
||||||
msg.append("[RO] ")
|
|
||||||
} else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
|
||||||
msg.append("[+] ")
|
|
||||||
}
|
|
||||||
|
|
||||||
val lline = editor.caretModel.logicalPosition.line
|
|
||||||
val total = IjVimEditor(editor).lineCount()
|
|
||||||
val pct = (lline.toFloat() / total.toFloat() * 100f + 0.5).toInt()
|
|
||||||
|
|
||||||
msg.append("line ").append(lline + 1).append(" of ").append(total)
|
|
||||||
msg.append(" --").append(pct).append("%-- ")
|
|
||||||
|
|
||||||
val lp = editor.caretModel.logicalPosition
|
|
||||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lline)
|
|
||||||
|
|
||||||
msg.append("col ").append(col + 1)
|
|
||||||
if (col != lp.column) {
|
|
||||||
msg.append("-").append(lp.column + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
VimPlugin.showMessage(msg.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun selectEditor(projectId: String, documentPath: String, protocol: String?): VimEditor? {
|
|
||||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol) ?: return null
|
|
||||||
val virtualFile = fileSystem.findFileByPath(documentPath) ?: return null
|
|
||||||
|
|
||||||
val project = Arrays.stream(ProjectManager.getInstance().openProjects)
|
|
||||||
.filter { p: Project? -> injector.file.getProjectId(p!!) == projectId }
|
|
||||||
.findFirst().orElseThrow()
|
|
||||||
|
|
||||||
val editor = selectEditor(project, virtualFile) ?: return null
|
|
||||||
return IjVimEditor(editor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getProjectId(project: Any): String {
|
|
||||||
require(project is Project)
|
|
||||||
return project.name + "-" + project.locationHash
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun findByNameInProject(filename: String, project: Project): VirtualFile? {
|
|
||||||
val projectScope = ProjectScope.getProjectScope(project)
|
|
||||||
val names = FilenameIndex.getVirtualFilesByName(filename, projectScope)
|
|
||||||
if (!names.isEmpty()) {
|
|
||||||
return names.stream().findFirst().get()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private val logger = Logger.getInstance(
|
|
||||||
FileGroup::class.java.name
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Respond to editor tab selection and remember the last used tab
|
|
||||||
*/
|
|
||||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
|
||||||
if (event.oldFile != null) {
|
|
||||||
getInstance(event.manager.project).lastTab = event.oldFile
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -19,22 +19,24 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
|||||||
* options
|
* options
|
||||||
*/
|
*/
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||||
var ide: String by optionProperty(IjOptions.ide)
|
public var ide: String by optionProperty(IjOptions.ide)
|
||||||
var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||||
var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||||
val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
public val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
||||||
var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
public var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
||||||
val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||||
var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||||
var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||||
|
|
||||||
// Temporary options to control work-in-progress behaviour
|
// Temporary options to control work-in-progress behaviour
|
||||||
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||||
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||||
var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||||
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||||
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||||
|
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||||
|
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,19 +44,8 @@ open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(sco
|
|||||||
*
|
*
|
||||||
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
||||||
*/
|
*/
|
||||||
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
|
public class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
|
||||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
||||||
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
||||||
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
||||||
var cursorline: Boolean by optionProperty(IjOptions.cursorline)
|
|
||||||
var fileformat: String by optionProperty(IjOptions.fileformat)
|
|
||||||
var list: Boolean by optionProperty(IjOptions.list)
|
|
||||||
var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
|
|
||||||
var textwidth: Int by optionProperty(IjOptions.textwidth)
|
|
||||||
var wrap: Boolean by optionProperty(IjOptions.wrap)
|
|
||||||
|
|
||||||
// IntelliJ specific options
|
|
||||||
var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
|
||||||
var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
|
||||||
var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
|
||||||
}
|
}
|
||||||
|
@@ -10,14 +10,9 @@ package com.maddyhome.idea.vim.group
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||||
import com.maddyhome.idea.vim.api.Options
|
import com.maddyhome.idea.vim.api.Options
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
import com.maddyhome.idea.vim.ex.exExceptionMessage
|
|
||||||
import com.maddyhome.idea.vim.options.NumberOption
|
|
||||||
import com.maddyhome.idea.vim.options.Option
|
import com.maddyhome.idea.vim.options.Option
|
||||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL
|
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL
|
||||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER
|
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER
|
||||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_BUFFER
|
|
||||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_WINDOW
|
|
||||||
import com.maddyhome.idea.vim.options.StringListOption
|
import com.maddyhome.idea.vim.options.StringListOption
|
||||||
import com.maddyhome.idea.vim.options.StringOption
|
import com.maddyhome.idea.vim.options.StringOption
|
||||||
import com.maddyhome.idea.vim.options.ToggleOption
|
import com.maddyhome.idea.vim.options.ToggleOption
|
||||||
@@ -26,9 +21,9 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
|||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
object IjOptions {
|
public object IjOptions {
|
||||||
|
|
||||||
fun initialise() {
|
public fun initialise() {
|
||||||
// Calling this method allows for deterministic initialisation of IjOptions, specifically initialising the
|
// Calling this method allows for deterministic initialisation of IjOptions, specifically initialising the
|
||||||
// properties and registering the IJ specific options. Once added, they can be safely accessed by name, e.g. by the
|
// properties and registering the IJ specific options. Once added, they can be safely accessed by name, e.g. by the
|
||||||
// implementation of `:set` while executing ~/.ideavimrc
|
// implementation of `:set` while executing ~/.ideavimrc
|
||||||
@@ -38,64 +33,15 @@ object IjOptions {
|
|||||||
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
|
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
public val ide: StringOption = addOption(
|
||||||
val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
|
|
||||||
val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
|
|
||||||
override fun checkIfValueValid(value: VimDataType, token: String) {
|
|
||||||
super.checkIfValueValid(value, token)
|
|
||||||
if (value != VimString.EMPTY) {
|
|
||||||
// Each element in the comma-separated string list needs to be a number. No spaces. Vim supports numbers
|
|
||||||
// beginning "+" or "-" to draw a highlight column relative to the 'textwidth' value. We don't fully support
|
|
||||||
// that, but we do automatically add "+0" because IntelliJ always displays the right margin
|
|
||||||
split((value as VimString).asString()).forEach {
|
|
||||||
if (!it.matches(Regex("[+-]?[0-9]+"))) {
|
|
||||||
throw exExceptionMessage("E474", token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
|
|
||||||
val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
|
|
||||||
val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
|
|
||||||
val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
|
|
||||||
val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
|
|
||||||
|
|
||||||
// These options are not explicitly listed as local-noglobal in Vim's help, but are set when a new buffer is edited,
|
|
||||||
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file conversion, we treat them as
|
|
||||||
// local-noglobal. See `:help local-noglobal`, `:help 'fileformats'` and `:help 'fileencodings'`
|
|
||||||
val bomb: ToggleOption =
|
|
||||||
addOption(ToggleOption("bomb", LOCAL_TO_BUFFER, "bomb", false, isLocalNoGlobal = true))
|
|
||||||
val fileencoding: StringOption = addOption(
|
|
||||||
StringOption(
|
|
||||||
"fileencoding",
|
|
||||||
LOCAL_TO_BUFFER,
|
|
||||||
"fenc",
|
|
||||||
VimString.EMPTY,
|
|
||||||
isLocalNoGlobal = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val fileformat: StringOption = addOption(
|
|
||||||
StringOption(
|
|
||||||
"fileformat",
|
|
||||||
LOCAL_TO_BUFFER,
|
|
||||||
"ff",
|
|
||||||
if (injector.systemInfoService.isWindows) "dos" else "unix",
|
|
||||||
boundedValues = setOf("dos", "unix", "mac"),
|
|
||||||
isLocalNoGlobal = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// IntelliJ specific functionality - custom options
|
|
||||||
val ide: StringOption = addOption(
|
|
||||||
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
||||||
)
|
)
|
||||||
val ideacopypreprocess: ToggleOption = addOption(
|
public val ideacopypreprocess: ToggleOption = addOption(
|
||||||
ToggleOption("ideacopypreprocess", GLOBAL_OR_LOCAL_TO_BUFFER, "ideacopypreprocess", false)
|
ToggleOption("ideacopypreprocess", GLOBAL_OR_LOCAL_TO_BUFFER, "ideacopypreprocess", false)
|
||||||
)
|
)
|
||||||
val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
public val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
||||||
val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
public val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
||||||
val idearefactormode: StringOption = addOption(
|
public val idearefactormode: StringOption = addOption(
|
||||||
StringOption(
|
StringOption(
|
||||||
"idearefactormode",
|
"idearefactormode",
|
||||||
GLOBAL_OR_LOCAL_TO_BUFFER,
|
GLOBAL_OR_LOCAL_TO_BUFFER,
|
||||||
@@ -104,7 +50,7 @@ object IjOptions {
|
|||||||
IjOptionConstants.ideaRefactorModeValues
|
IjOptionConstants.ideaRefactorModeValues
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val ideastatusicon: StringOption = addOption(
|
public val ideastatusicon: StringOption = addOption(
|
||||||
StringOption(
|
StringOption(
|
||||||
"ideastatusicon",
|
"ideastatusicon",
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
@@ -113,7 +59,7 @@ object IjOptions {
|
|||||||
IjOptionConstants.ideaStatusIconValues
|
IjOptionConstants.ideaStatusIconValues
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val ideavimsupport: StringListOption = addOption(
|
public val ideavimsupport: StringListOption = addOption(
|
||||||
StringListOption(
|
StringListOption(
|
||||||
"ideavimsupport",
|
"ideavimsupport",
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
@@ -122,26 +68,27 @@ object IjOptions {
|
|||||||
IjOptionConstants.ideavimsupportValues
|
IjOptionConstants.ideavimsupportValues
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@JvmField
|
@JvmField public val ideawrite: StringOption = addOption(
|
||||||
val ideawrite: StringOption = addOption(
|
|
||||||
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
||||||
)
|
)
|
||||||
val lookupkeys: StringListOption = addOption(
|
public val lookupkeys: StringListOption = addOption(
|
||||||
StringListOption(
|
StringListOption(
|
||||||
"lookupkeys",
|
"lookupkeys",
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
"lookupkeys",
|
"lookupkeys",
|
||||||
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
||||||
)
|
)
|
||||||
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||||
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||||
|
|
||||||
// Temporary feature flags during development, not really intended for external use
|
// Temporary feature flags during development, not really intended for external use
|
||||||
val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||||
val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||||
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
||||||
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||||
val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||||
|
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
||||||
|
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||||
|
|
||||||
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
||||||
// derives from Option<VimInt>
|
// derives from Option<VimInt>
|
||||||
|
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.maddyhome.idea.vim.group
|
|
||||||
|
|
||||||
import com.intellij.lang.CodeDocumentationAwareCommenter
|
|
||||||
import com.intellij.lang.LanguageCommenters
|
|
||||||
import com.intellij.openapi.components.Service
|
|
||||||
import com.intellij.psi.PsiComment
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
|
||||||
import com.maddyhome.idea.vim.api.VimPsiService
|
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
|
||||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
|
||||||
|
|
||||||
@Service
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.maddyhome.idea.vim.group
|
|
||||||
|
|
||||||
import com.intellij.openapi.editor.event.DocumentEvent
|
|
||||||
import com.intellij.openapi.editor.event.DocumentListener
|
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.api.VimRedrawService
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
|
||||||
|
|
||||||
class IjVimRedrawService : VimRedrawService {
|
|
||||||
override fun redraw() {
|
|
||||||
// The only thing IntelliJ needs to redraw is the status line. Everything else is handled automatically.
|
|
||||||
redrawStatusLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun redrawStatusLine() {
|
|
||||||
injector.messages.clearStatusBarMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Simulate Vim's redraw when the current editor changes
|
|
||||||
*/
|
|
||||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
|
||||||
injector.redrawService.redraw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulates Vim's redraw when the document changes
|
|
||||||
*
|
|
||||||
* Only redraw if lines are added/removed.
|
|
||||||
*/
|
|
||||||
object RedrawListener : DocumentListener {
|
|
||||||
override fun documentChanged(event: DocumentEvent) {
|
|
||||||
if (VimPlugin.isNotEnabled()) return
|
|
||||||
if (event.newFragment.contains("\n") || event.oldFragment.contains("\n")) {
|
|
||||||
injector.redrawService.redraw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,25 +11,41 @@ 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.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
|
||||||
@@ -38,9 +54,12 @@ 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
|
||||||
@@ -48,11 +67,17 @@ 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.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
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
|
||||||
|
|
||||||
@@ -65,6 +90,24 @@ 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,
|
||||||
@@ -87,12 +130,85 @@ 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)
|
||||||
@@ -103,7 +219,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()
|
val bufferLine = caret.getLine().line
|
||||||
return editor.getLeadingCharacterOffset(bufferLine, col)
|
return editor.getLeadingCharacterOffset(bufferLine, col)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +232,36 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -133,18 +279,30 @@ 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
|
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||||
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
|
return editor.currentCaret().offset.point
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||||
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
|
return editor.currentCaret().offset.point
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun moveCaretToLinePercent(
|
||||||
|
editor: VimEditor,
|
||||||
|
caret: ImmutableVimCaret,
|
||||||
|
count: Int,
|
||||||
|
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||||
|
return moveCaretToLineWithStartOfLineOption(
|
||||||
|
editor,
|
||||||
|
editor.normalizeLine((editor.lineCount() * clamp(count, 0, 100) + 99) / 100 - 1),
|
||||||
|
caret,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class ScreenLocation {
|
private enum class ScreenLocation {
|
||||||
@@ -301,21 +459,16 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||||
|
ExEntryPanel.deactivateAll()
|
||||||
val fileEditor = event.oldEditor
|
val fileEditor = event.oldEditor
|
||||||
if (fileEditor is TextEditor) {
|
if (fileEditor is TextEditor) {
|
||||||
val editor = fileEditor.editor
|
val editor = fileEditor.editor
|
||||||
if (!editor.isDisposed) {
|
if (!editor.isDisposed) {
|
||||||
|
ExOutputModel.getInstance(editor).clear()
|
||||||
editor.vim.let { vimEditor ->
|
editor.vim.let { vimEditor ->
|
||||||
when (vimEditor.mode) {
|
if (VimStateMachine.getInstance(vimEditor).mode is Mode.VISUAL) {
|
||||||
is Mode.VISUAL -> {
|
vimEditor.exitVisualMode()
|
||||||
vimEditor.exitVisualMode()
|
KeyHandler.getInstance().reset(vimEditor)
|
||||||
KeyHandler.getInstance().reset(vimEditor)
|
|
||||||
}
|
|
||||||
is Mode.CMD_LINE -> {
|
|
||||||
injector.processGroup.cancelExEntry(vimEditor, refocusOwningEditor = false, resetCaret = false)
|
|
||||||
ExOutputModel.tryGetInstance(editor)?.close()
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,28 +12,181 @@ import com.intellij.execution.configurations.GeneralCommandLine
|
|||||||
import com.intellij.execution.process.CapturingProcessHandler
|
import com.intellij.execution.process.CapturingProcessHandler
|
||||||
import com.intellij.execution.process.ProcessAdapter
|
import com.intellij.execution.process.ProcessAdapter
|
||||||
import com.intellij.execution.process.ProcessEvent
|
import com.intellij.execution.process.ProcessEvent
|
||||||
|
import com.intellij.openapi.diagnostic.debug
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
import com.intellij.util.execution.ParametersListUtil
|
import com.intellij.util.execution.ParametersListUtil
|
||||||
import com.intellij.util.text.CharSequenceReader
|
import com.intellij.util.text.CharSequenceReader
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
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.VimProcessGroupBase
|
import com.maddyhome.idea.vim.api.VimProcessGroupBase
|
||||||
import com.maddyhome.idea.vim.api.globalOptions
|
import com.maddyhome.idea.vim.api.globalOptions
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.command.Command
|
||||||
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
|
import com.maddyhome.idea.vim.ex.InvalidCommandException
|
||||||
|
import com.maddyhome.idea.vim.helper.requestFocus
|
||||||
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode.NORMAL
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||||
|
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
|
||||||
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||||
|
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||||
import java.io.BufferedWriter
|
import java.io.BufferedWriter
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
import java.io.Writer
|
import java.io.Writer
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
|
public class ProcessGroup : VimProcessGroupBase() {
|
||||||
|
override var lastCommand: String? = null
|
||||||
|
private set
|
||||||
|
override var isCommandProcessing: Boolean = false
|
||||||
|
override var modeBeforeCommandProcessing: Mode? = null
|
||||||
|
|
||||||
|
public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) {
|
||||||
|
// Don't allow searching in one line editors
|
||||||
|
if (editor.isOneLineMode()) return
|
||||||
|
|
||||||
|
val initText = ""
|
||||||
|
val label = leader.toString()
|
||||||
|
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.activate(editor.ij, context.ij, label, initText, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun endSearchCommand(): String {
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.deactivate(true)
|
||||||
|
|
||||||
|
return panel.text
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun startExCommand(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||||
|
// Don't allow ex commands in one line editors
|
||||||
|
if (editor.isOneLineMode()) return
|
||||||
|
|
||||||
|
val currentMode = editor.vimStateMachine.mode
|
||||||
|
check(currentMode is ReturnableFromCmd) {
|
||||||
|
"Cannot enable cmd mode from current mode $currentMode"
|
||||||
|
}
|
||||||
|
|
||||||
|
isCommandProcessing = true
|
||||||
|
modeBeforeCommandProcessing = currentMode
|
||||||
|
val initText = getRange(editor, cmd)
|
||||||
|
injector.markService.setVisualSelectionMarks(editor)
|
||||||
|
editor.mode = Mode.CMD_LINE(currentMode)
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||||
|
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
||||||
|
// is open. So I'll put focus back in the editor and process the key.
|
||||||
|
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
if (panel.isActive) {
|
||||||
|
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||||
|
requestFocus(panel.entry)
|
||||||
|
panel.handleKey(stroke)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||||
|
lambdaEditor.mode = NORMAL()
|
||||||
|
getInstance().reset(lambdaEditor)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun processExEntry(editor: VimEditor, context: ExecutionContext): Boolean {
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.deactivate(true)
|
||||||
|
var res = true
|
||||||
|
try {
|
||||||
|
editor.mode = NORMAL()
|
||||||
|
|
||||||
|
logger.debug("processing command")
|
||||||
|
|
||||||
|
val text = panel.text
|
||||||
|
|
||||||
|
if (panel.label != ":") {
|
||||||
|
// Search is handled via Argument.Type.EX_STRING. Although ProcessExEntryAction is registered as the handler for
|
||||||
|
// <CR> in both command and search modes, it's only invoked for command mode (see KeyHandler.handleCommandNode).
|
||||||
|
// We should never be invoked for anything other than an actual ex command.
|
||||||
|
throw InvalidCommandException("Expected ':' command. Got '" + panel.label + "'", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug {
|
||||||
|
"swing=" + SwingUtilities.isEventDispatchThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
injector.vimscriptExecutor.execute(text, editor, context, skipHistory(editor), true, CommandLineVimLContext)
|
||||||
|
} catch (e: ExException) {
|
||||||
|
VimPlugin.showMessage(e.message)
|
||||||
|
VimPlugin.indicateError()
|
||||||
|
res = false
|
||||||
|
} catch (bad: Exception) {
|
||||||
|
logger.error(bad)
|
||||||
|
VimPlugin.indicateError()
|
||||||
|
res = false
|
||||||
|
} finally {
|
||||||
|
isCommandProcessing = false
|
||||||
|
modeBeforeCommandProcessing = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// commands executed from map command / macro should not be added to history
|
||||||
|
private fun skipHistory(editor: VimEditor): Boolean {
|
||||||
|
return getInstance(editor).mappingState.isExecutingMap() || injector.macro.isExecutingMacro
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
|
||||||
|
editor.mode = NORMAL()
|
||||||
|
getInstance().reset(editor)
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.deactivate(true, resetCaret)
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun startFilterCommand(editor: VimEditor, context: ExecutionContext, cmd: Command) {
|
||||||
|
val initText = getRange(editor, cmd) + "!"
|
||||||
|
val currentMode = editor.mode
|
||||||
|
check(currentMode is ReturnableFromCmd) { "Cannot enable cmd mode from $currentMode" }
|
||||||
|
editor.mode = Mode.CMD_LINE(currentMode)
|
||||||
|
val panel = ExEntryPanel.getInstance()
|
||||||
|
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRange(editor: VimEditor, cmd: Command): String {
|
||||||
|
var initText = ""
|
||||||
|
if (editor.vimStateMachine.mode is VISUAL) {
|
||||||
|
initText = "'<,'>"
|
||||||
|
} else if (cmd.rawCount > 0) {
|
||||||
|
initText = if (cmd.count == 1) {
|
||||||
|
"."
|
||||||
|
} else {
|
||||||
|
".,.+" + (cmd.count - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initText
|
||||||
|
}
|
||||||
|
|
||||||
class ProcessGroup : VimProcessGroupBase() {
|
|
||||||
@Throws(ExecutionException::class, ProcessCanceledException::class)
|
@Throws(ExecutionException::class, ProcessCanceledException::class)
|
||||||
override fun executeCommand(
|
public override fun executeCommand(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
command: String,
|
command: String,
|
||||||
input: CharSequence?,
|
input: CharSequence?,
|
||||||
@@ -132,7 +285,7 @@ class ProcessGroup : VimProcessGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
public companion object {
|
||||||
private val logger = logger<ProcessGroup>()
|
private val logger = logger<ProcessGroup>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.helper.ScrollViewHelper
|
|||||||
import com.maddyhome.idea.vim.helper.StrictMode
|
import com.maddyhome.idea.vim.helper.StrictMode
|
||||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||||
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
||||||
|
import com.maddyhome.idea.vim.helper.vimEditorGroup
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
|
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener
|
||||||
@@ -257,7 +258,11 @@ internal class ScrollGroup : VimScrollGroup {
|
|||||||
|
|
||||||
object ScrollOptionsChangeListener : EffectiveOptionValueChangeListener {
|
object ScrollOptionsChangeListener : EffectiveOptionValueChangeListener {
|
||||||
override fun onEffectiveValueChanged(editor: VimEditor) {
|
override fun onEffectiveValueChanged(editor: VimEditor) {
|
||||||
editor.ij.apply { ScrollViewHelper.scrollCaretIntoView(this) }
|
editor.ij.apply {
|
||||||
|
if (vimEditorGroup) {
|
||||||
|
ScrollViewHelper.scrollCaretIntoView(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1507
src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
Normal file
1507
src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,6 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
|||||||
jumpElem.setAttribute("line", jump.line.toString())
|
jumpElem.setAttribute("line", jump.line.toString())
|
||||||
jumpElem.setAttribute("column", jump.col.toString())
|
jumpElem.setAttribute("column", jump.col.toString())
|
||||||
jumpElem.setAttribute("filename", StringUtil.notNullize(jump.filepath))
|
jumpElem.setAttribute("filename", StringUtil.notNullize(jump.filepath))
|
||||||
jumpElem.setAttribute("protocol", StringUtil.notNullize(jump.protocol))
|
|
||||||
projectElement.addContent(jumpElem)
|
projectElement.addContent(jumpElem)
|
||||||
if (logger.isDebug()) {
|
if (logger.isDebug()) {
|
||||||
logger.debug("saved jump = $jump")
|
logger.debug("saved jump = $jump")
|
||||||
@@ -74,7 +73,6 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
|||||||
Integer.parseInt(jumpElement.getAttributeValue("line")),
|
Integer.parseInt(jumpElement.getAttributeValue("line")),
|
||||||
Integer.parseInt(jumpElement.getAttributeValue("column")),
|
Integer.parseInt(jumpElement.getAttributeValue("column")),
|
||||||
jumpElement.getAttributeValue("filename"),
|
jumpElement.getAttributeValue("filename"),
|
||||||
jumpElement.getAttributeValue("protocol", "file"),
|
|
||||||
)
|
)
|
||||||
jumps.add(jump)
|
jumps.add(jump)
|
||||||
}
|
}
|
||||||
@@ -96,7 +94,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
|||||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||||
// we do not want jumps that were processed before
|
// we do not want jumps that were processed before
|
||||||
val jump = buildJump(changePlace) ?: return
|
val jump = buildJump(changePlace) ?: return
|
||||||
jumpService.addJump(injector.file.getProjectId(project), jump, true)
|
jumpService.addJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +106,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
|||||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||||
// we do not want jumps that were processed before
|
// we do not want jumps that were processed before
|
||||||
val jump = buildJump(changePlace) ?: return
|
val jump = buildJump(changePlace) ?: return
|
||||||
jumpService.removeJump(injector.file.getProjectId(project), jump)
|
jumpService.removeJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +120,6 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
|||||||
|
|
||||||
val path = place.file.path
|
val path = place.file.path
|
||||||
|
|
||||||
return Jump(line, col, path, place.file.fileSystem.protocol)
|
return Jump(line, col, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -83,11 +83,6 @@ internal class PutGroup : VimPutBase() {
|
|||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
val context = vimContext.context as DataContext
|
val context = vimContext.context as DataContext
|
||||||
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
||||||
if (editor.isInsertMode) {
|
|
||||||
val undo = injector.undo
|
|
||||||
val nanoTime = System.nanoTime()
|
|
||||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
|
||||||
}
|
|
||||||
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
|
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
|
||||||
val startOffset =
|
val startOffset =
|
||||||
prepareDocumentAndGetStartOffsets(
|
prepareDocumentAndGetStartOffsets(
|
||||||
|
@@ -26,11 +26,9 @@ import com.maddyhome.idea.vim.listener.VimListenerManager
|
|||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.options.OptionConstants
|
import com.maddyhome.idea.vim.options.OptionConstants
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.inCommandLineMode
|
|
||||||
import com.maddyhome.idea.vim.state.mode.inNormalMode
|
import com.maddyhome.idea.vim.state.mode.inNormalMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
|
||||||
@@ -69,11 +67,6 @@ internal object IdeaSelectionControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (editor.selectionModel.hasSelection(true)) {
|
if (editor.selectionModel.hasSelection(true)) {
|
||||||
if (editor.vim.inCommandLineMode && editor.vim.mode.returnTo().hasVisualSelection) {
|
|
||||||
logger.trace { "Modifying selection while in Command-line mode, most likely incsearch" }
|
|
||||||
return@singleTask
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dontChangeMode(editor)) {
|
if (dontChangeMode(editor)) {
|
||||||
IdeaRefactorModeHelper.correctSelection(editor)
|
IdeaRefactorModeHelper.correctSelection(editor)
|
||||||
logger.trace { "Selection corrected for refactoring" }
|
logger.trace { "Selection corrected for refactoring" }
|
||||||
@@ -124,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.getEditorExecutionContext(editor.vim))
|
.insertBeforeCursor(editor.vim, injector.executionContextManager.onEditor(editor.vim))
|
||||||
|
|
||||||
is Mode.NORMAL -> Unit
|
is Mode.NORMAL -> Unit
|
||||||
else -> error("Unexpected mode: $mode")
|
else -> error("Unexpected mode: $mode")
|
||||||
|
@@ -53,12 +53,12 @@ import javax.swing.Timer
|
|||||||
* no adjustment gets performed and IdeaVim stays in insert mode.
|
* no adjustment gets performed and IdeaVim stays in insert mode.
|
||||||
*/
|
*/
|
||||||
// Do not remove until it's used in EasyMotion plugin in tests
|
// Do not remove until it's used in EasyMotion plugin in tests
|
||||||
object VimVisualTimer {
|
public object VimVisualTimer {
|
||||||
|
|
||||||
var swingTimer: Timer? = null
|
public var swingTimer: Timer? = null
|
||||||
var mode: Mode? = null
|
public var mode: Mode? = null
|
||||||
|
|
||||||
inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
|
public inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
|
||||||
swingTimer?.stop()
|
swingTimer?.stop()
|
||||||
|
|
||||||
if (mode == null) mode = currentMode
|
if (mode == null) mode = currentMode
|
||||||
@@ -70,7 +70,7 @@ object VimVisualTimer {
|
|||||||
swingTimer = timer
|
swingTimer = timer
|
||||||
}
|
}
|
||||||
|
|
||||||
fun doNow() {
|
public fun doNow() {
|
||||||
val swingTimer1 = swingTimer
|
val swingTimer1 = swingTimer
|
||||||
if (swingTimer1 != null) {
|
if (swingTimer1 != null) {
|
||||||
swingTimer1.stop()
|
swingTimer1.stop()
|
||||||
@@ -80,12 +80,12 @@ object VimVisualTimer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun drop() {
|
public fun drop() {
|
||||||
swingTimer?.stop()
|
swingTimer?.stop()
|
||||||
swingTimer = null
|
swingTimer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
|
public inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
|
||||||
task(mode)
|
task(mode)
|
||||||
swingTimer = null
|
swingTimer = null
|
||||||
mode = null
|
mode = null
|
||||||
|
@@ -9,9 +9,13 @@
|
|||||||
package com.maddyhome.idea.vim.group.visual
|
package com.maddyhome.idea.vim.group.visual
|
||||||
|
|
||||||
import com.intellij.find.FindManager
|
import com.intellij.find.FindManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
|
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
|
||||||
|
import com.maddyhome.idea.vim.command.CommandState
|
||||||
|
import com.maddyhome.idea.vim.command.engine
|
||||||
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.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,4 +31,12 @@ internal class VisualMotionGroup : VimVisualMotionGroupBase() {
|
|||||||
|
|
||||||
return super.autodetectVisualSubmode(editor)
|
return super.autodetectVisualSubmode(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPATIBILITY-LAYER: Added a method
|
||||||
|
* Please see: https://jb.gg/zo8n0r
|
||||||
|
*/
|
||||||
|
fun enterVisualMode(editor: Editor, subMode: CommandState.SubMode? = null): Boolean {
|
||||||
|
return this.enterVisualMode(editor.vim, subMode?.engine)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 = dataContext?.vim ?: injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
val context = injector.executionContextManager.onEditor(editor.vim, dataContext?.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)
|
||||||
}
|
}
|
||||||
|
@@ -73,7 +73,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Editor.guicursorMode(): GuiCursorMode {
|
private fun Editor.guicursorMode(): GuiCursorMode {
|
||||||
return GuiCursorMode.fromMode(vim.mode, injector.vimState.isReplaceCharacter)
|
return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,8 +90,6 @@ 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)
|
||||||
}
|
}
|
||||||
@@ -144,20 +142,26 @@ private object AttributesCache {
|
|||||||
@TestOnly
|
@TestOnly
|
||||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
||||||
|
|
||||||
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
|
public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
|
||||||
override fun isReplaceCharChanged(editor: VimEditor) {
|
override fun isReplaceCharChanged(editor: VimEditor) {
|
||||||
updateCaretsVisual()
|
updateCaretsVisual(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
||||||
updateCaretsVisual()
|
updateCaretsVisual(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCaretsVisual() {
|
private fun updateCaretsVisual(editor: VimEditor) {
|
||||||
updateAllEditorsCaretsVisual()
|
if (injector.globalOptions().ideaglobalmode) {
|
||||||
|
updateAllEditorsCaretsVisual()
|
||||||
|
} else {
|
||||||
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
|
ijEditor.updateCaretsVisualAttributes()
|
||||||
|
ijEditor.updateCaretsVisualPosition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAllEditorsCaretsVisual() {
|
public fun updateAllEditorsCaretsVisual() {
|
||||||
injector.editorGroup.getEditors().forEach { editor ->
|
injector.editorGroup.getEditors().forEach { editor ->
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
ijEditor.updateCaretsVisualAttributes()
|
ijEditor.updateCaretsVisualAttributes()
|
||||||
|
@@ -1,37 +1,31 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
*
|
*
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE.txt file or at
|
* license that can be found in the LICENSE.txt file or at
|
||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.ui.ex
|
|
||||||
|
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.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.VimCommandLine
|
|
||||||
import com.maddyhome.idea.vim.api.VimCommandLineService
|
|
||||||
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.helper.TestInputModel
|
|
||||||
import com.maddyhome.idea.vim.helper.inRepeatMode
|
|
||||||
import com.maddyhome.idea.vim.helper.isCloseKeyStroke
|
|
||||||
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
|
||||||
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
class ExEntryPanelService : VimCommandLineService {
|
@Service
|
||||||
override fun getActiveCommandLine(): VimCommandLine? {
|
internal class CommandLineHelper : VimCommandLineHelper {
|
||||||
val instance = ExEntryPanel.instance ?: ExEntryPanel.instanceWithoutShortcuts ?: return null
|
|
||||||
return if (instance.isActive) instance else null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inputString(vimEditor: VimEditor, context: ExecutionContext, prompt: String, finishOn: Char?): String? {
|
override fun inputString(vimEditor: VimEditor, prompt: String, finishOn: Char?): String? {
|
||||||
val editor = vimEditor.ij
|
val editor = vimEditor.ij
|
||||||
if (vimEditor.inRepeatMode) {
|
if (vimEditor.vimStateMachine.isDotRepeatInProgress) {
|
||||||
val input = Extension.consumeString()
|
val input = Extension.consumeString()
|
||||||
return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}")
|
return input ?: error("Not enough strings saved: ${Extension.lastExtensionHandler}")
|
||||||
}
|
}
|
||||||
@@ -58,26 +52,27 @@ class ExEntryPanelService : VimCommandLineService {
|
|||||||
} else {
|
} else {
|
||||||
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 commandLine = injector.commandLine.create(vimEditor, context, prompt.ifEmpty { " " }, "")
|
val exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts()
|
||||||
|
exEntryPanel.activate(editor, injector.executionContextManager.onEditor(editor.vim).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() -> {
|
||||||
commandLine.deactivate(refocusOwningEditor = true, resetCaret = true)
|
exEntryPanel.deactivate(true)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
key.keyCode == KeyEvent.VK_ENTER -> {
|
key.keyCode == KeyEvent.VK_ENTER -> {
|
||||||
text = commandLine.actualText
|
text = exEntryPanel.text
|
||||||
commandLine.deactivate(refocusOwningEditor = true, resetCaret = true)
|
exEntryPanel.deactivate(true)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
finishOn != null && key.keyChar == finishOn -> {
|
finishOn != null && key.keyChar == finishOn -> {
|
||||||
commandLine.handleKey(key)
|
exEntryPanel.handleKey(key)
|
||||||
text = commandLine.actualText
|
text = exEntryPanel.text
|
||||||
commandLine.deactivate(refocusOwningEditor = true, resetCaret = true)
|
exEntryPanel.deactivate(true)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
commandLine.handleKey(key)
|
exEntryPanel.handleKey(key)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,20 +83,4 @@ class ExEntryPanelService : VimCommandLineService {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun create(editor: VimEditor, context: ExecutionContext, label: String, initText: String): VimCommandLine {
|
|
||||||
val panel = ExEntryPanel.getInstance()
|
|
||||||
panel.activate(editor.ij, context.ij, label, initText)
|
|
||||||
return panel
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createWithoutShortcuts(editor: VimEditor, context: ExecutionContext, label: String, initText: String): VimCommandLine {
|
|
||||||
val panel = ExEntryPanel.getInstanceWithoutShortcuts()
|
|
||||||
panel.activate(editor.ij, context.ij, label, initText)
|
|
||||||
return panel
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fullReset() {
|
|
||||||
ExEntryPanel.fullReset()
|
|
||||||
}
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user