1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-18 10:31:44 +02:00

Compare commits

..

21 Commits

Author SHA1 Message Date
8de7a9f741 Set plugin version to chylex-27 2024-02-16 06:09:40 +01:00
5977840cef Disable speed search in Project tool window when NERDTree is enabled 2024-02-16 06:09:40 +01:00
d0fea229c4 Work around VIM-3306 (make paragraph-motion plugin go to the end of the line) 2024-02-16 06:09:40 +01:00
9eeac4a788 Apply scrolloff after executing native IDEA actions 2024-02-16 06:09:40 +01:00
8370661b0d Stay on same line after reindenting 2024-02-16 06:09:40 +01:00
3f88e64abf Implement motions to go to next/previous misspelled word 2024-02-16 06:09:39 +01:00
28e3a7c894 Update search register when using f/t 2024-02-16 06:09:39 +01:00
bc859c65f5 Automatically add unambiguous imports after running a macro 2024-02-16 06:09:39 +01:00
141fc8ed22 Fix(VIM-3179): Respect virtual space below editor (imperfectly) 2024-02-16 06:09:39 +01:00
dec494639c Fix(VIM-3178): Workaround to support "Jump to Source" action mapping 2024-02-16 06:09:39 +01:00
d06688033b Fix(VIM-3166): Workaround to fix broken filtering of visual lines 2024-02-16 06:09:39 +01:00
98110b9132 Add support for count for visual and line motion surround 2024-02-16 06:09:39 +01:00
a0404b8664 Fix vim-surround not working with multiple cursors
Fixes multiple cursors with vim-surround commands `cs, ds, S` (but not `ys`).
2024-02-16 06:09:39 +01:00
a2a30d545e Fix(VIM-696) Restore visual mode after undo/redo, and disable incompatible actions 2024-02-16 06:09:38 +01:00
4f3bf039c9 Revert(VIM-2884): Fix moving lines to cursor 2024-02-15 08:30:02 +01:00
1f4b97fa00 Respect count with <Action> mappings 2024-02-15 08:30:02 +01:00
61301fdbf5 Add Matchit support for Java statements 2024-02-15 08:30:02 +01:00
c980250529 Change matchit plugin to use HTML patterns in unrecognized files 2024-02-15 08:30:02 +01:00
bdb4b02016 Reset insert mode when switching active editor 2024-02-15 08:30:02 +01:00
62039a00d3 Remove update checker 2024-02-15 08:30:02 +01:00
95c66fd4a6 Set custom plugin version 2024-02-15 08:30:01 +01:00
1131 changed files with 80972 additions and 123376 deletions

View File

@@ -7,9 +7,6 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: [ master ] branches: [ master ]
permissions:
contents: write
jobs: jobs:
build: build:
@@ -23,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
@@ -37,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 }}

View File

@@ -20,11 +20,6 @@ on:
schedule: schedule:
- cron: '44 12 * * 4' - cron: '44 12 * * 4'
permissions:
actions: read
contents: read
security-events: write
jobs: jobs:
analyze: analyze:
name: Analyze name: Analyze

View File

@@ -18,16 +18,16 @@ 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
- name: Run tests - name: Run tests
run: ./gradlew --no-configuration-cache integrationsTest run: ./gradlew integrationsTest
env: env:
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }} YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }} GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -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

View File

@@ -11,7 +11,7 @@ on:
jobs: jobs:
build: build:
if: false if: github.event.pull_request.merged == true && github.repository == 'JetBrains/ideavim'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -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 }}

View File

@@ -9,21 +9,28 @@ jobs:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Apply Patch
run: |
git apply tests/ui-ij-tests/src/test/kotlin/ui/octopus.patch
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
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
# - name: Setup Gradle with:
# uses: gradle/gradle-build-action@v2.4.2 # Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin - name: Build Plugin
run: gradle :buildPlugin run: gradle :buildPlugin
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle --no-configuration-cache runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log & gradle runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@@ -37,7 +44,7 @@ jobs:
run: mv tests/ui-ij-tests/video build/reports run: mv tests/ui-ij-tests/video build/reports
- name: Move sandbox logs - name: Move sandbox logs
if: always() if: always()
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report - name: Save report
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -45,8 +52,7 @@ jobs:
name: ui-test-fails-report-mac name: ui-test-fails-report-mac
path: | path: |
build/reports build/reports
tests/ui-ij-tests/build/reports sandbox-idea-log
idea-sandbox-log
# build-for-ui-test-linux: # build-for-ui-test-linux:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
# steps: # steps:
@@ -78,4 +84,4 @@ jobs:
# with: # with:
# name: ui-test-fails-report-linux # name: ui-test-fails-report-linux
# path: | # path: |
# ui-test-example/build/reports # ui-test-example/build/reports

View File

@@ -18,15 +18,19 @@ 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
# - name: Setup Gradle with:
# uses: gradle/gradle-build-action@v2.4.2 # Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin - name: Build Plugin
run: gradle :buildPlugin run: gradle :buildPlugin
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle --no-configuration-cache :runIdeForUiTests -PideaType=PC > build/reports/idea.log & gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@@ -40,7 +44,7 @@ jobs:
run: mv tests/ui-py-tests/video build/reports run: mv tests/ui-py-tests/video build/reports
- name: Move sandbox logs - name: Move sandbox logs
if: always() if: always()
run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report - name: Save report
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -48,5 +52,4 @@ jobs:
name: ui-test-fails-report-mac name: ui-test-fails-report-mac
path: | path: |
build/reports build/reports
tests/ui-py-tests/build/reports sandbox-idea-log
idea-sandbox-log

View File

@@ -15,15 +15,19 @@ 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
# - name: Setup Gradle with:
# uses: gradle/gradle-build-action@v2.4.2 # Not strictly necessary, but it may prevent rate limit
# errors especially on GitHub-hosted macos machines.
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.4.2
- name: Build Plugin - name: Build Plugin
run: gradle :buildPlugin run: gradle :buildPlugin
- name: Run Idea - name: Run Idea
run: | run: |
mkdir -p build/reports mkdir -p build/reports
gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log & gradle runIdeForUiTests > build/reports/idea.log &
- name: Wait for Idea started - name: Wait for Idea started
uses: jtalk/url-health-check-action@v3 uses: jtalk/url-health-check-action@v3
with: with:
@@ -37,7 +41,7 @@ jobs:
run: mv tests/ui-ij-tests/video build/reports run: mv tests/ui-ij-tests/video build/reports
- name: Move sandbox logs - name: Move sandbox logs
if: always() if: always()
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log run: mv build/idea-sandbox/system/log sandbox-idea-log
- name: Save report - name: Save report
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -45,8 +49,7 @@ jobs:
name: ui-test-fails-report-mac name: ui-test-fails-report-mac
path: | path: |
build/reports build/reports
tests/ui-ij-tests/build/reports sandbox-idea-log
idea-sandbox-log
# build-for-ui-test-linux: # build-for-ui-test-linux:
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
# steps: # steps:
@@ -78,4 +81,4 @@ jobs:
# with: # with:
# name: ui-test-fails-report-linux # name: ui-test-fails-report-linux
# path: | # path: |
# ui-test-example/build/reports # ui-test-example/build/reports

View File

@@ -10,9 +10,6 @@ on:
push: push:
branches: [ master ] branches: [ master ]
permissions:
contents: write
jobs: jobs:
build: build:
@@ -37,17 +34,6 @@ jobs:
id: update_authors id: update_authors
run: cp -a origin/doc/. docs run: cp -a origin/doc/. docs
# The Wiki for github should have no `.md` in references
# Otherwise, such links will lead to the raw text.
# However, the `.md` should exist to have a navigation in GitHub code.
- name: Replace `.md)` with `)`
run: |
# Define the directory you want to process
DIRECTORY="docs"
# Find all files in the directory and perform the replacement
find $DIRECTORY -type f -exec sed -i 's/\.md)/)/g' {} +
- name: Commit changes - name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4 uses: stefanzweifel/git-auto-commit-action@v4
with: with:

View 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 }}

View File

@@ -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 }}

View File

@@ -7,12 +7,15 @@ on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '0 10 * * *' - cron: '0 10 * * *'
# Workflow run on push is disabled to avoid conflicts when merging PR
# push:
# branches: [ master ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: false if: github.repository == 'JetBrains/ideavim'
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -22,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
@@ -36,7 +39,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 }}
@@ -60,4 +63,4 @@ jobs:
# dependabot updates. See mergeDependatobPR.yml file. # dependabot updates. See mergeDependatobPR.yml file.
# However, it turned out that GitHub accepts pushes from the actions as a PR and requires checks, that are always # However, it turned out that GitHub accepts pushes from the actions as a PR and requires checks, that are always
# false for pushing from actions. # false for pushing from actions.
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227 # This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227

View File

@@ -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

10
.gitignore vendored
View File

@@ -1,6 +1,5 @@
*.swp *.swp
/.gradle/ /.gradle/
/.intellijPlatform/
/.idea/ /.idea/
!/.idea/scopes !/.idea/scopes
@@ -11,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/
@@ -25,10 +22,11 @@
.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
/vim-engine/src/main/resources/ksp-generated
/src/main/resources/ksp-generated
# Created by github automation # Created by github automation
settings.xml settings.xml
.kotlin

1
.idea/.name generated
View File

@@ -1 +0,0 @@
IdeaVim

22
.idea/misc.xml generated
View File

@@ -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>

View File

@@ -12,7 +12,7 @@
<option name="taskNames"> <option name="taskNames">
<list> <list>
<option value="check" /> <option value="check" />
<option value="verifyPlugin" /> <option value="runPluginVerifier" />
</list> </list>
</option> </option>
<option name="vmOptions" value="" /> <option name="vmOptions" value="" />
@@ -20,7 +20,6 @@
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess> <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess> <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled> <DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" /> <method v="2" />
</configuration> </configuration>
</component> </component>

View File

@@ -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
View File

@@ -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>

View File

@@ -5,12 +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 NVIM_TESTS = "2024.2.1" const val GITHUB_TESTS = "2023.3.2"
const val PROPERTY_TESTS = "2024.2.1" const val NVIM_TESTS = "2023.3.2"
const val LONG_RUNNING_TESTS = "2024.2.1" const val PROPERTY_TESTS = "2023.3.2"
const val QODANA_TESTS = "2024.2.1" const val LONG_RUNNING_TESTS = "2023.3.2"
const val RELEASE = "2024.2.1" const val QODANA_TESTS = "2023.3.2"
const val RELEASE = "2023.3.2"
const val RELEASE_DEV = "2024.2.1" const val RELEASE_DEV = "2023.3.2"
const val RELEASE_EAP = "2024.2.1" const val RELEASE_EAP = "2023.3.2"
} }

View File

@@ -8,24 +8,23 @@ import _Self.buildTypes.PropertyBased
import _Self.buildTypes.Qodana import _Self.buildTypes.Qodana
import _Self.buildTypes.TestingBuildType import _Self.buildTypes.TestingBuildType
import _Self.subprojects.GitHub import _Self.subprojects.GitHub
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
object Project : Project({ object Project : Project({
description = "Vim engine for JetBrains IDEs" description = "Vim engine for JetBrains IDEs"
subProjects(Releases, GitHub) subProjects(Releases, OldTests, GitHub)
// 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.2.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)
@@ -42,9 +41,6 @@ object Project : Project({
abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({ abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
artifactRules = """ artifactRules = """
+:build/reports => build/reports +:build/reports => build/reports
+:tests/java-tests/build/reports => java-tests/build/reports
+:tests/long-running-tests/build/reports => long-running-tests/build/reports
+:tests/property-tests/build/reports => property-tests/build/reports
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/ +:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
""".trimIndent() """.trimIndent()
@@ -54,7 +50,7 @@ abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
// These requirements define Linux-Medium configuration. // These requirements define Linux-Medium configuration.
// Unfortunately, requirement by name (teamcity.agent.name) doesn't work // Unfortunately, requirement by name (teamcity.agent.name) doesn't work
// IDK the reason for it, but on our agents this property is empty // IDK the reason for it, but on our agents this property is empty
equals("teamcity.agent.hardware.cpuCount", "16") equals("teamcity.agent.hardware.cpuCount", "4")
equals("teamcity.agent.os.family", "Linux") equals("teamcity.agent.os.family", "Linux")
} }

View File

@@ -37,12 +37,9 @@ object Compatibility : IdeaVimBuildType({
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city # Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
# java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
""".trimIndent() """.trimIndent()
} }
} }

View File

@@ -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"
}
}
})

View File

@@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
steps { steps {
gradle { gradle {
tasks = "clean verifyPlugin" tasks = "clean runPluginVerifier"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
} }

View File

@@ -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"
}
}
})

View File

@@ -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
} }

View File

@@ -46,8 +46,8 @@ object Qodana : IdeaVimBuildType({
version = Qodana.JVMVersion.LATEST version = Qodana.JVMVersion.LATEST
} }
reportAsTests = true reportAsTests = true
additionalDockerArguments = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs"
additionalQodanaArguments = "--baseline qodana.sarif.json" additionalQodanaArguments = "--baseline qodana.sarif.json"
cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
} }
} }
@@ -63,6 +63,7 @@ object Qodana : IdeaVimBuildType({
timezone = "SERVER" timezone = "SERVER"
} }
param("dayOfWeek", "Sunday") param("dayOfWeek", "Sunday")
enabled = false
} }
} }

View File

@@ -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()
}
}
}
})

View File

@@ -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")
@@ -95,14 +97,14 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Set TeamCity build number" name = "Set TeamCity build number"
tasks = "scripts:setTeamCityBuildNumber" tasks = "scripts:setTeamCityBuildNumber"
} }
// gradle { gradle {
// name = "Update change log" name = "Update change log"
// tasks = "scripts:changelogUpdateUnreleased" tasks = "scripts:changelogUpdateUnreleased"
// } }
// gradle { gradle {
// name = "Commit preparation changes" name = "Commit preparation changes"
// tasks = "scripts:commitChanges" tasks = "scripts:commitChanges"
// } }
gradle { gradle {
name = "Add release tag" name = "Add release tag"
tasks = "scripts:addReleaseTag" tasks = "scripts:addReleaseTag"
@@ -115,24 +117,33 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
name = "Publish release" name = "Publish release"
tasks = "publishPlugin" tasks = "publishPlugin"
} }
// script { script {
// name = "Checkout master branch" name = "Checkout master branch"
// scriptContent = """ scriptContent = """
// echo Checkout master echo Checkout master
// git checkout master git checkout master
// """.trimIndent() """.trimIndent()
// } }
// gradle { gradle {
// name = "Update change log in master" name = "Update change log in master"
// tasks = "scripts:changelogUpdateUnreleased" tasks = "scripts:changelogUpdateUnreleased"
// } }
// gradle { gradle {
// name = "Commit preparation changes in master" name = "Commit preparation changes in master"
// tasks = "scripts:commitChanges" tasks = "scripts:commitChanges"
// } }
script { script {
name = "Push changes to the repo" name = "Push changes to the repo"
scriptContent = """ scriptContent = """
branch=$(git branch --show-current)
echo Current branch is ${'$'}branch
if [ "master" != "${'$'}branch" ];
then
git checkout master
fi
git push origin
git checkout release git checkout release
echo checkout release branch echo checkout release branch
git branch --set-upstream-to=origin/release release git branch --set-upstream-to=origin/release release
@@ -144,12 +155,11 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
gradle { gradle {
name = "Run Integrations" name = "Run Integrations"
tasks = "releaseActions" tasks = "releaseActions"
gradleParams = "--no-configuration-cache"
} }
// gradle { gradle {
// name = "Slack Notification" name = "Slack Notification"
// tasks = "slackNotification" tasks = "slackNotification"
// } }
} }
features { features {
@@ -157,4 +167,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()
}
}
}
}) })

View File

@@ -39,11 +39,9 @@ open class TestingBuildType(
steps { steps {
gradle { gradle {
clearConditions()
tasks = "clean test" tasks = "clean test"
buildFile = "" buildFile = ""
enableStacktrace = true enableStacktrace = true
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
} }
} }

View File

@@ -1,5 +1,6 @@
package _Self.subprojects package _Self.subprojects
import _Self.Constants
import _Self.IdeaVimBuildType import _Self.IdeaVimBuildType
import _Self.vcsRoots.GitHubPullRequest import _Self.vcsRoots.GitHubPullRequest
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
@@ -15,15 +16,16 @@ 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"
params { params {
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false") param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
param("env.ORG_GRADLE_PROJECT_ideaVersion", Constants.GITHUB_TESTS)
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false") param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
} }

25
.teamcity/_Self/subprojects/OldTests.kt vendored Normal file
View File

@@ -0,0 +1,25 @@
package _Self.subprojects
import _Self.buildTypes.TestingBuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
object OldTests : Project({
name = "Old IdeaVim tests"
description = "Tests for older versions of IJ"
buildType(TestingBuildType("IC-2018.1", "181-182", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2018.2", "181-182", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2018.3", "183", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2019.1", "191-193", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2019.2", "191-193", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2019.3", "191-193", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2020.1", "201", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2020.2", "202", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2020.3", "203-212", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2021.1", "203-212", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2021.2.2", "203-212", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2021.3.2", "213-221", javaVersion = "1.8", javaPlugin = false))
buildType(TestingBuildType("IC-2022.2.3", branch = "222", javaPlugin = false))
buildType(TestingBuildType("IC-2023.1", "231-232", javaPlugin = false))
buildType(TestingBuildType("IC-2023.2", "231-232", javaPlugin = false))
})

View File

@@ -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)
}) })

View File

@@ -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"
}
})

View File

@@ -0,0 +1,29 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.changeBuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
expectSteps {
gradle {
tasks = "clean test"
buildFile = ""
enableStacktrace = true
}
}
steps {
update<GradleBuildStep>(0) {
clearConditions()
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
}
}
}

View File

@@ -0,0 +1,19 @@
package patches.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
/*
This patch script was generated by TeamCity on settings change in UI.
To apply the patch, change the buildType with id = 'PublishVimEngine'
accordingly, and delete the patch script.
*/
changeBuildType(RelativeId("PublishVimEngine")) {
vcs {
check(branchFilter == "+:<default>") {
"Unexpected option value: branchFilter = $branchFilter"
}
branchFilter = "+:fleet"
}
}

View File

@@ -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)

View File

@@ -495,74 +495,6 @@ Contributors:
[![icon][github]](https://github.com/emanuelgestosa) [![icon][github]](https://github.com/emanuelgestosa)
&nbsp; &nbsp;
Emanuel Gestosa Emanuel Gestosa
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
[![icon][github]](https://github.com/lippfi)
&nbsp;
lippfi,
* [![icon][mail]](mailto:fillipser143@gmail.com)
[![icon][github]](https://github.com/Parker7123)
&nbsp;
FilipParker
* [![icon][mail]](mailto:7138209+duhaesbaert@users.noreply.github.com)
[![icon][github]](https://github.com/duhaesbaert)
&nbsp;
Eduardo Haesbaert
* [![icon][mail]](mailto:nikolaevsky.egor@gmail.com)
[![icon][github]](https://github.com/Aisper)
&nbsp;
Egor Nikolaevsky
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
[![icon][github]](https://github.com/kun-codes)
&nbsp;
Bishwa Saha,
* [![icon][mail]](mailto:alexfu@fastmail.com)
[![icon][github]](https://github.com/alexfu)
&nbsp;
Alex Fu
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
[![icon][github]](https://github.com/LazyScaper)
&nbsp;
Jake
* [![icon][mail]](mailto:the1xdeveloper@gmail.com)
[![icon][github]](https://github.com/The1xDeveloper)
&nbsp;
The1xDeveloper
* [![icon][mail]](mailto:shaunewilliams@gmail.com)
[![icon][github]](https://github.com/shaunlebron)
&nbsp;
shaun
* [![icon][mail]](mailto:i.i.babko@gmail.com)
[![icon][github]](https://github.com/igorbabko)
&nbsp;
Igor Babko
* [![icon][mail]](mailto:533601+felixwiemuth@users.noreply.github.com)
[![icon][github]](https://github.com/felixwiemuth)
&nbsp;
Felix Wiemuth
* [![icon][mail]](mailto:kirill.karnaukhov@jetbrains.com)
[![icon][github]](https://github.com/kkarnauk)
&nbsp;
Kirill Karnaukhov,
* [![icon][mail]](mailto:sander.hestvik@gmail.com)
[![icon][github]](https://github.com/SanderHestvik)
&nbsp;
SanderHestvik
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
[![icon][github]](https://github.com/gregsh)
&nbsp;
Greg Shrago
* [![icon][mail]](mailto:jphalip@gmail.com)
[![icon][github]](https://github.com/jphalip)
&nbsp;
Julien Phalip
* [![icon][mail]](mailto:j.trimailovas@gmail.com)
[![icon][github]](https://github.com/trimailov)
&nbsp;
Justas Trimailovas,
* [![icon][mail]](mailto:justast@wix.com)
[![icon][github]](https://github.com/justast-wix)
&nbsp;
Justas Trimailovas
Previous contributors: Previous contributors:
@@ -574,10 +506,6 @@ Previous contributors:
[![icon][github]](https://github.com/kevin70) [![icon][github]](https://github.com/kevin70)
&nbsp; &nbsp;
kk kk
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
[![icon][github]](https://github.com/gregsh)
&nbsp;
Greg Shrago
If you are a contributor and your name is not listed here, feel free to If you are a contributor and your name is not listed here, feel free to

View File

@@ -23,19 +23,13 @@ It is important to distinguish EAP from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even Please note that the quality of EAP versions may at times be way below even
usual beta standards. usual beta standards.
## End of changelog file maintenance ## To Be Released
Since version 2.9.0, the changelog can be found on YouTrack
* [To Be Released](https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20)
* [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
## 2.9.0, 2024-02-20
### Fixes: ### Fixes:
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot * [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
### Merged PRs: ### Merged PRs:
* [725](https://github.com/JetBrains/ideavim/pull/725) by [Emanuel Gestosa](https://github.com/emanuelgestosa): Regex
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro * [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
## 2.8.0, 2024-01-30 ## 2.8.0, 2024-01-30

View File

@@ -62,16 +62,12 @@ for a few days or send it to a friend for testing.
If you are looking for: If you are looking for:
- Vim commands (`w`, `<C-O>`, `p`, etc.): - Vim commands (`w`, `<C-O>`, `p`, etc.):
- Any particular command: - Any particular command: `package-info.java`.
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_commands.json)
- How commands are executed in common: `EditorActionHandlerBase`. - How commands are executed in common: `EditorActionHandlerBase`.
- Key mapping: `KeyHandler.handleKey()`. - Key mapping: `KeyHandler.handleKey()`.
- Ex commands (`:set`, `:s`, `:nohlsearch`): - Ex commands (`:set`, `:s`, `:nohlsearch`):
- Any particular command: - Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`.
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_ex_commands.json)
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_ex_commands.json)
- Vim script grammar: `Vimscript.g4`. - Vim script grammar: `Vimscript.g4`.
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`. - Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
- Vim script executor: `Executor`. - Vim script executor: `Executor`.

View File

@@ -109,6 +109,7 @@ etc
See also: See also:
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes) * [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md) * [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD) * [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
@@ -221,13 +222,13 @@ Ex commands or via `:map` command mappings:
* Execute an action by `{action_id}`. Works from Ex command line. * Execute an action by `{action_id}`. Works from Ex command line.
* Please don't use `:action` in mappings. Use `<Action>` instead. * Please don't use `:action` in mappings. Use `<Action>` instead.
### Finding action IDs: ### Finding action ids:
* IJ provides `IdeaVim: track action IDs` command to show the id of the executed actions. * IJ provides `IdeaVim: track action Ids` command to show the id of the executed actions.
This command can be found in "Search everywhere" (double `shift`). This command can be found in "Search everywhere" (double `shift`).
<details> <details>
<summary><strong>"Track action IDs" Details</strong> (click to see)</summary> <summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif"> <source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
<img src="assets/readme/track_action_light.gif" alt="track action ids"/> <img src="assets/readme/track_action_light.gif" alt="track action ids"/>
@@ -368,8 +369,6 @@ is the full list of synonyms.
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27): - Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
> The local value is set to -123456 when the global value is to be used. > The local value is set to -123456 when the global value is to be used.
- Vi (not Vim) is a POSIX standard, and [has a spec](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html)! Vim is mostly POSIX compliant when Vi compatibility is selected with the `'compatible'` option, but there are still some differences that can be changed with `'copoptions'`. The spec is interesting because it documents the behaviour of different commands in a stricter style than the user documentation, describing the current line and column after the command, for example. [More details can be found by reading `:help posix`](https://vimhelp.org/vi_diff.txt.html#posix).
License License
------- -------

View File

@@ -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.

View File

@@ -8,7 +8,7 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
kotlin("plugin.serialization") version "2.0.0" kotlin("plugin.serialization") version "1.9.22"
} }
val kotlinxSerializationVersion: String by project val kotlinxSerializationVersion: String by project
@@ -21,7 +21,7 @@ repositories {
} }
dependencies { dependencies {
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.0-1.0.29") compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
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")

View File

@@ -37,8 +37,7 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!) val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` })) val fileContent = json.encodeToString(commands)
val fileContent = json.encodeToString(sortedCommands)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -37,8 +37,7 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!) val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap() val fileContent = json.encodeToString(commandToClass)
val fileContent = json.encodeToString(sortedCommandToClass)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -37,8 +37,7 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
Files.createDirectories(generatedDirPath) Files.createDirectories(generatedDirPath)
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!) val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap() val fileContent = json.encodeToString(nameToClass)
val fileContent = json.encodeToString(sortedNameToClass)
filePath.writeText(fileContent) filePath.writeText(fileContent)
return emptyList() return emptyList()

View File

@@ -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)
} }

View File

@@ -32,8 +32,7 @@ import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.RepositoryBuilder import org.eclipse.jgit.lib.RepositoryBuilder
import org.intellij.markdown.ast.getTextInNode import org.intellij.markdown.ast.getTextInNode
import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.Changelog
import org.jetbrains.intellij.platform.gradle.TestFrameworkType import org.jetbrains.changelog.exceptions.MissingVersionException
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
import org.kohsuke.github.GHUser import org.kohsuke.github.GHUser
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
@@ -45,19 +44,19 @@ buildscript {
} }
dependencies { dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2") classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
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:7.1.0.202411261347-r") classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
classpath("org.kohsuke:github-api:1.305") classpath("org.kohsuke:github-api:1.305")
classpath("io.ktor:ktor-client-core:3.0.2") classpath("io.ktor:ktor-client-core:2.3.7")
classpath("io.ktor:ktor-client-cio:3.0.2") classpath("io.ktor:ktor-client-cio:2.3.7")
classpath("io.ktor:ktor-client-auth:3.0.2") classpath("io.ktor:ktor-client-auth:2.3.7")
classpath("io.ktor:ktor-client-content-negotiation:3.0.2") classpath("io.ktor:ktor-client-content-negotiation:2.3.7")
classpath("io.ktor:ktor-serialization-kotlinx-json:3.0.2") classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
// 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")
@@ -65,25 +64,44 @@ buildscript {
} }
plugins { plugins {
antlr
java java
kotlin("jvm") version "2.0.0" kotlin("jvm") version "1.9.22"
application application
id("java-test-fixtures") id("java-test-fixtures")
id("org.jetbrains.intellij.platform") version "2.2.0"
id("org.jetbrains.changelog") version "2.2.1" id("org.jetbrains.intellij") version "1.17.1"
id("org.jetbrains.changelog") version "2.2.0"
id("org.jetbrains.kotlinx.kover") version "0.6.1" id("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 "2.0.0-1.0.23"
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
} }
val moduleSources by configurations.registering 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
val ideaVersion: String by project val ideaVersion: String by project
val ideaType: String by project val ideaType: 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 publishChannels: String by project val publishChannels: String by project
@@ -92,49 +110,25 @@ 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()
intellijPlatform { maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
defaultRepositories()
}
} }
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:26.0.1") compileOnly("org.jetbrains:annotations:24.1.0")
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
intellijPlatform { antlr("org.antlr:antlr4:$antlrVersion")
// Snapshots don't use installers
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
// Note that it is also possible to use local("...") to compile against a locally installed IDE
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
// Or something like: intellijIdeaUltimate(ideaVersion)
create(ideaType, ideaVersion, useInstaller)
pluginVerifier()
zipSigner()
instrumentationTools()
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
plugin("AceJump", "3.8.19")
plugin("com.intellij.classic.ui", "242.20224.159")
}
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
// --------- Test dependencies ---------- // --------- Test dependencies ----------
testImplementation(testFixtures(project(":")))
testApi("com.squareup.okhttp3:okhttp:4.12.0") testApi("com.squareup.okhttp3:okhttp:4.12.0")
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
@@ -148,20 +142,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.4.0") testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5") testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5") testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5") testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5") testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5") testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
// Can be removed when IJPL-159134 is fixed
// testRuntimeOnly("junit:junit:4.13.2")
testImplementation("org.junit.vintage:junit-vintage-engine:5.10.5")
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
} }
configurations { configurations {
@@ -172,8 +160,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
@@ -186,9 +172,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
@@ -201,91 +184,29 @@ tasks {
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library // See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
// For the list of bundled versions // For the list of bundled versions
apiVersion = "1.9" apiVersion = "1.9"
freeCompilerArgs = listOf( freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
"-Xjvm-default=all-compatibility",
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
"-Xskip-prerelease-check",
"-Xallow-unstable-dependencies",
)
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
} }
compileTestKotlin { compileTestKotlin {
enabled = false
kotlinOptions { kotlinOptions {
jvmTarget = javaVersion jvmTarget = javaVersion
apiVersion = "1.9" apiVersion = "1.9"
// Needed to compile the AceJump which uses kotlin beta
// Without these two option compilation fails
freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
// allWarningsAsErrors = true // allWarningsAsErrors = true
} }
} }
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use downloadRobotServerPlugin {
// a custom task (see below) version.set(remoteRobotVersion)
runIde {
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
} }
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies runIdeForUiTests {
// Note that the version must be greater than the plugin's target version, for obvious reasons systemProperty("robot-server.port", "8082")
// You can also set splitMode and splitModeTarget here to test split mode in a custom IDE systemProperty("ide.mac.message.dialogs.as.sheets", "false")
// val runIdeCustom by intellijPlatformTesting.runIde.registering { systemProperty("jb.privacy.policy.text", "<!--999.999-->")
// type = IntelliJPlatformType.Rider systemProperty("jb.consents.confirmation.enabled", "false")
// version = "2024.1.2" systemProperty("ide.show.tips.on.startup.default.value", "false")
// }
// Uncomment to run the plugin in a locally installed IDE
// val runIdeLocal by intellijPlatformTesting.runIde.registering {
// localPath = file("/Users/{user}/Applications/WebStorm.app")
// }
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
task {
jvmArgumentProviders += CommandLineArgumentProvider {
listOf(
"-Drobot-server.port=8082",
"-Dide.mac.message.dialogs.as.sheets=false",
"-Djb.privacy.policy.text=<!--999.999-->",
"-Djb.consents.confirmation.enabled=false",
"-Dide.show.tips.on.startup.default.value=false",
"-Doctopus.handler=" + (System.getProperty("octopus.handler") ?: true),
)
}
}
plugins {
robotServerPlugin(remoteRobotVersion)
}
}
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
splitMode = true
splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
}
// Add plugin open API sources to the plugin ZIP
val sourcesJar by registering(Jar::class) {
dependsOn(moduleSources)
destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveClassifier.set(DocsType.SOURCES)
from(sourceSets.main.map { it.kotlin })
from(provider {
moduleSources.map {
it.map { jarFile -> zipTree(jarFile) }
}
})
}
buildPlugin {
dependsOn(sourcesJar)
from(sourcesJar) { into("lib/src") }
} }
} }
@@ -296,6 +217,7 @@ java {
} }
kotlin { kotlin {
explicitApi()
jvmToolchain { jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(javaVersion)) languageVersion.set(JavaLanguageVersion.of(javaVersion))
} }
@@ -310,68 +232,109 @@ gradle.projectsEvaluated {
// --- Intellij plugin // --- Intellij plugin
intellijPlatform { intellij {
pluginConfiguration { version.set(ideaVersion)
name = "IdeaVim" type.set(ideaType)
changeNotes.set( pluginName.set("IdeaVim")
"""
Undo in IdeaVim now works like in Vim<br/>
Caret movement is no longer a separate undo step, and full insert is undoable in one step.<br/>
<a href="https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks">Share Feedback</a>
<br/>
<br/>
<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
""".trimIndent()
)
ideaVersion { updateSinceUntilBuild.set(false)
// Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against
// specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified.
// The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it
// with a null provider (the provider is important).
// By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of
// IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile
// against, we can never get an API that's newer - it would be an unresolved symbol.
untilBuild.set(provider { null })
}
}
publishing { downloadSources.set(downloadIdeaSources.toBoolean())
instrumentCode.set(instrumentPluginCode.toBoolean())
intellijRepository.set("https://www.jetbrains.com/intellij-repository")
plugins.set(listOf("AceJump:3.8.11"))
}
tasks {
publishPlugin {
channels.set(publishChannels.split(",")) channels.set(publishChannels.split(","))
token.set(publishToken) token.set(publishToken)
} }
signing { signPlugin {
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN")) certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
privateKey.set(providers.environmentVariable("PRIVATE_KEY")) privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
} }
verifyPlugin { runPluginVerifier {
teamCityOutputFormat = true downloadDir.set("${project.buildDir}/pluginVerifier/ides")
ides { teamCityOutputFormat.set(true)
recommended() // ideVersions.set(listOf("IC-2021.3.4"))
}
} }
instrumentCode.set(instrumentPluginCode.toBoolean()) 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") }
}
val pluginVersion = version
// Don't forget to update plugin.xml
patchPluginXml {
// Get the latest available change notes from the changelog file
changeNotes.set(
provider {
with(changelog) {
val log = try {
getUnreleased()
} catch (e: MissingVersionException) {
getOrNull(pluginVersion.toString()) ?: getLatest()
}
renderItem(
log,
org.jetbrains.changelog.Changelog.OutputType.HTML,
)
}
},
)
}
} }
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 {
@@ -466,14 +429,12 @@ val prId: String by project
tasks.register("updateMergedPr") { tasks.register("updateMergedPr") {
doLast { doLast {
val x = changelog.getUnreleased() if (project.hasProperty("prId")) {
println("x") println("Got pr id: $prId")
// if (project.hasProperty("prId")) { updateMergedPr(prId.toInt())
// println("Got pr id: $prId") } else {
// updateMergedPr(prId.toInt()) error("Cannot get prId")
// } else { }
// error("Cannot get prId")
// }
} }
} }
@@ -497,9 +458,7 @@ 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")
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")
setYoutrackStatus(tickets, "Fixed") setYoutrackStatus(tickets, "Fixed")
@@ -958,12 +917,12 @@ fun changes(): List<Change> {
println("Start changes processing") println("Start changes processing")
for (message in messages) { for (message in messages) {
println("Processing '$message'...") println("Processing '$message'...")
val lowercaseMessage = message.lowercase() val lowercaseMessage = message.toLowerCase()
val regex = "^fix\\((vim-\\d+)\\):".toRegex() val regex = "^fix\\((vim-\\d+)\\):".toRegex()
val findResult = regex.find(lowercaseMessage) val findResult = regex.find(lowercaseMessage)
if (findResult != null) { if (findResult != null) {
println("Message matches") println("Message matches")
val value = findResult.groups[1]!!.value.uppercase() val value = findResult.groups[1]!!.value.toUpperCase()
val shortMessage = message.drop(findResult.range.last + 1).trim() val shortMessage = message.drop(findResult.range.last + 1).trim()
newFixes += Change(value, shortMessage) newFixes += Change(value, shortMessage)
} else { } else {

View File

@@ -129,26 +129,8 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
</details> </details>
### 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>
@@ -321,10 +303,7 @@ If you want to optimize highlight duration, assign a time in milliseconds:
If you want to change background color of highlight you can provide the rgba of the color you want e.g. If you want to change background color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"` `let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
</details> </details>
@@ -438,50 +417,3 @@ Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
</details> </details>
<details>
<summary><h2>Vim Peekaboo</h2></summary>
By Julien Phalip
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
### Setup
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
</details>
<details>
<summary><h2>FunctionTextObj</h2></summary>
By Julien Phalip
### Setup
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
</details>
<details>
<summary><h2>Switch</h2></summary>
By Julien Phalip
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
### Setup
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
or restart the IDE.
### Instructions
https://plugins.jetbrains.com/plugin/25899-vim-switch
</details>

View File

@@ -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

View File

@@ -5,9 +5,9 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
1. Install the plugin via Marketplace 1. Install the plugin via Marketplace
2. Enable action tracking. You can enable it by one of the following ways: 2. Enable action tracking. You can enable it by one of the following ways:
* Execute `:set trackactionids` ex command or just `:set tai` * Execute `:set trackactionids` ex command or just `:set tai`
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action IDs" to find the toggle that enables and disables action tracking * Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action Ids" to find the toggle that enables and disables action tracking
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner: 3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
<img alt="Notification" src="images/action-id-notification.png"/> <img alt="Notification" src="images/action-id-notification.png"/>
4. Copy the action id from the notification to create the following mapping in your .ideavimrc 4. Copy the action id from the notification to create the following mapping in your .ideavimrc
```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)``` ```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)```

View File

@@ -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 behavior 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
'gdefault' 'gd' The ":substitute" flag 'g' is by default
'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>, `ideaput` (default on) - IdeaVim ONLY
<PageUp> and <PageDown>. enable native idea paste action for put operations
'digraph' 'dg' enable the entering of digraphs in Insert mode
'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
`keymodel` `km` String (default "continueselect,stopselect")
'matchpairs' 'mps' Pairs of characters that "%" can match List of comma separated words, which enable special things that keys
'maxmapdepth' 'mmd' Maximum depth of mappings can do. These values can be used:
'more' 'more' When on, listings pause when the whole screen is filled startsel Using a shifted special[1] key starts selection (either
'nrformats' 'nf' Number formats recognized for CTRL-A command Select mode or Visual mode, depending on "key" being
'operatorfunc' 'opfunc' Name of a function to call with the g@ operator present in 'selectmode').
'scroll' 'scr' Number of lines to scroll with CTRL-U and CTRL-D stopsel Using a NOT-shifted special[1] key stops selection.
'selection' 'sel' What type of selection to use Automatically enables `stopselect` and `stopvisual`
'selectmode' 'slm' Controls when to start Select mode instead of Visual stopselect Using a NOT-shifted special[1] key stops - IdeaVim ONLY
This is a comma-separated list of words: select mode and removes selection.
stopvisual Using a NOT-shifted special[1] key stops - IdeaVim ONLY
mouse When using the mouse visual mode and removes selection.
key When using shifted special[1] keys continueselect Using a shifted arrow key doesn't - IdeaVim ONLY
cmd When using "v", "V", or <C-V> start selection, but in select mode
ideaselection When IDE sets a selection - IdeaVim ONLY acts like startsel is enabled
(e.g.: extend selection, wrap with while, etc.) continuevisual Using a shifted arrow key doesn't - IdeaVim ONLY
start selection, but in visual mode
acts like startsel is enabled
'matchpairs' 'mps' pairs of characters that "%" can match
'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
'number' 'nu' print the line number in front of each line
'relativenumber' 'rnu' show the line number relative to the line with
the cursor
'scroll' 'scr' lines to scroll with CTRL-U and CTRL-D
'scrolljump' 'sj' minimum number of lines to scroll
'scrolloff' 'so' minimum number of lines above and below the cursor
'selection' 'sel' what type of selection to use
'shell' 'sh' The shell to use to execute commands with ! and :! `selectmode` `slm` String (default "")
'shellcmdflag' 'shcf' The command flag passed to the shell
'shellxescape' 'sxe' The characters to be escaped when calling a shell
'shellxquote' 'sxq' The quote character to use in a shell command
'showcmd' 'sc' Show (partial) command in the status bar
'showmode' 'smd' Show the current mode in the status bar
'smartcase' 'scs' Use case sensitive search if any character in the
pattern is uppercase
'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)
'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 This is a comma-separated list of words, which specify when to start
'whichwrap' 'ww' Which keys that move the cursor left/right can wrap to Select mode instead of Visual mode, when a selection is started.
other lines Possible values:
A comma-separated list of these flags: mouse when using the mouse
char key modes key when using shifted special[1] keys
b <BS> Normal and Visual cmd when using "v", "V", or <C-V>
s <Space> Normal and Visual ideaselection when IDE sets a selection - IdeaVim ONLY
h "h" Normal and Visual (examples: extend selection, wrap with while, etc.)
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 `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 options mapped to IntelliJ-based IDE settings 'showmode' 'smd' message on the status line to show current mode
'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
IdeaVim only commands:
IdeaVim provides its own implementation for handling scroll jump and offset, even though IntelliJ-based IDEs have similar functionality (there are differences in behaviour). `ideamarks` `ideamarks` Boolean (default true)
When IdeaVim is hosted in an IntelliJ-based IDE (but not JetBrains Fleet), the following options map to the equivalent IDE settings:
If true, creation of global mark will trigger creation of IDE's bookmark
and vice versa.
`idearefactormode` `idearefactormode` String(default "select")
Define the mode that would be enabled during
the refactoring (renaming, live template, introduce variable, etc)
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.
Visual or select mode are not changed.
`ideajoin` `ideajoin` Boolean (default false)
If true, join command will be performed via IDE
See wiki/`ideajoin` examples
`ideastatusicon` `ideastatusicon` String(default "enabled")
Define the behavior of IdeaVim icon in the status bar.
Use one of the following values:
- enabled - icon is shown in the status bar
- gray - use the gray version of the icon
- disabled - hide the icon
``` `ideawrite` `ideawrite` String (default "all")
'scrolljump' 'sj' Minimal number of lines to scroll "file" or "all". Defines the behaviour of ":w" command.
'scrolloff' 'so' Minimal number of lines above and below the cursor Value "all" enables execution of ":wa" (save all) command on ":w" (save).
'sidescroll' 'ss' Minimal number of columns to scroll horizontally This feature exists because some IJ options like "Prettier on save" or "ESlint on save"
'sidescrolloff' 'siso' Minimal number of columns to left and right of cursor work only with "save all" action. If this option is set to "all", these actions work
``` also with ":w" command.
`lookupkeys` `lookupkeys` List of strings
List of keys that should be processed by the IDE during the active lookup (autocompletion).
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>"
`ideavimsupport` `ideavimsupport` List of strings (default "dialog")
Define the list of additional buffers where IdeaVim is enabled.
- dialog - enable IdeaVim in dialogs
- singleline - enable IdeaVim in single line editors (not suggested)
## IdeaVim options for IntelliJ-based IDE features ----------
[1] - cursor keys, <End>, <Home>, <PageUp> and <PageDown>
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.
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.
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 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).
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.
In other words, if the local Vim value is explicitly set for a window or buffer, interactively, then it should not be reset.
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.
(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.
```
'bomb' 'bomb' Add or remove a byte order mark (BOM) to the
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.
They control integration with the host IDE.
Unless otherwise stated, these options do not have abbreviations.
```
'ideacopypreprocess' boolean (default off)
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
value is off. The equivalent processing for paste is controlled by the
"ideaput" value to the 'clipboard' option.
'ideaglobalmode' boolean (default off)
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)
global or local to buffer
When enabled, join commands will be handled by the IDE's "smart join"
feature. The IDE can change syntax when joining lines, such as merging
string literals or if statements. See the wiki for more examples. Not
all languages support smart join functionality.
'ideamarks' boolean (default on)
global
Maps Vim's global marks to IDE bookmarks.
'idearefactormode' string (default "select")
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,
Insert or Replace mode. Visual or Select modes are not changed.
'ideastatusicon' string (default "enabled")
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")
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
commit tool window, or inside dialogs, and even as single line fields.
'ideawrite' string (default "all")
global
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.
```

View File

@@ -1,7 +1,7 @@
# Support Guide # Support Guide
This document is created to help our support team. This document is created to help our support team.
It's not intended to be read by the users as it brings no value to them. It's not intended to be read by the users as it brings to value to them.
## Support channels ## Support channels
@@ -25,7 +25,7 @@ It's not intended to be read by the users as it brings no value to them.
IdeaVim has multiple YouTrack statuses, main are: IdeaVim has multiple YouTrack statuses, main are:
- Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets. - Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets.
- Open: issues is processed by our team, what means that the issues is reproduced and accepted - Open: issues is processed by out team, what means that the issues is reproduced and accepted
- Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the - Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the
user doesn't reply in 30 days. user doesn't reply in 30 days.
- Ready To Release: Bug is fixed, but not yet released - Ready To Release: Bug is fixed, but not yet released
@@ -35,4 +35,4 @@ IdeaVim has multiple YouTrack statuses, main are:
# ~.ideavimrc file # ~.ideavimrc file
`~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program, `~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program,
so it makes sense to additionally request this file in case the issues can't be reproduced. so it makes sense to additionally request this file in case the issues can't be reproduced.

View File

@@ -8,40 +8,33 @@
# 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=2023.3.3
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
# 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.2
# 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
instrumentPluginCode=true instrumentPluginCode=true
version=chylex-42 version=chylex-27
javaVersion=17 javaVersion=17
remoteRobotVersion=0.11.23 remoteRobotVersion=0.11.22
antlrVersion=4.10.1 antlrVersion=4.10.1
kotlin.incremental.useClasspathSnapshot=false
# 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
kotlinVersion=2.0.0 kotlinVersion=1.9.22
publishToken=token publishToken=token
publishChannels=eap publishChannels=eap
# Kotlinx serialization also uses some version of kotlin stdlib under the hood. However, # Kotlinx serialization also uses some version of kotlin stdlib under the hood. However,
# we exclude this version from the dependency and use our own version of kotlin that is specified above # we exclude this version from the dependency and use our own version of kotlin that is specified above
kotlinxSerializationVersion=1.6.2 kotlinxSerializationVersion=1.5.1
slackUrl= slackUrl=
youtrackToken= 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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

22
gradlew vendored
View File

@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -85,9 +83,7 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -148,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045 # shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@@ -156,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045 # shellcheck disable=SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -205,11 +201,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command;
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# and any embedded shellness will be escaped. # shell script including quotes and variable substitutions, so put them in
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # double quotes to make sure that they get re-expanded; and
# treated as '${Hostname}' itself on the command line. # * put everything else in single quotes, so that it's not re-expanded.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \

22
gradlew.bat vendored
View File

@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@@ -59,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail

File diff suppressed because one or more lines are too long

View File

@@ -19,10 +19,8 @@ exclude:
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/JavaText.kt
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt - src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated - src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
- vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated - src/main/java/com/maddyhome/idea/vim/package-info.java
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
- tests/ui-fixtures
dependencyIgnores: dependencyIgnores:
- name: "acejump" - name: "acejump"
- name: "icu4j" - name: "icu4j"

View File

@@ -20,17 +20,17 @@ repositories {
} }
dependencies { dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.0") compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
implementation("io.ktor:ktor-client-core:3.0.2") implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-cio:3.0.2") implementation("io.ktor:ktor-client-cio:2.3.7")
implementation("io.ktor:ktor-client-content-negotiation:3.0.2") implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.2") implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
implementation("io.ktor:ktor-client-auth:3.0.2") implementation("io.ktor:ktor-client-auth:2.3.7")
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:7.1.0.202411261347-r") implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-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")

View File

@@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
*/ */
@Suppress("SpellCheckingInspection") @Suppress("SpellCheckingInspection")
val knownPlugins = setOf( val knownPlugins = listOf(
"IdeaVimExtension", "IdeaVimExtension",
"github.zgqq.intellij-enhance", "github.zgqq.intellij-enhance",
"org.jetbrains.IdeaVim-EasyMotion", "org.jetbrains.IdeaVim-EasyMotion",
@@ -31,18 +31,10 @@ val knownPlugins = setOf(
"com.github.copilot", "com.github.copilot",
"com.github.dankinsoid.multicursor", "com.github.dankinsoid.multicursor",
"com.joshestein.ideavim-quickscope", "com.joshestein.ideavim-quickscope",
"ca.alexgirard.HarpoonIJ", "ca.alexgirard.HarpoonIJ",
"me.kyren223.harpoonforjb", // https://plugins.jetbrains.com/plugin/23771-harpoonforjb
"com.github.erotourtes.harpoon", // https://plugins.jetbrains.com/plugin/21796-harpooner
"me.kyren223.trident", // https://plugins.jetbrains.com/plugin/23818-trident
"com.protoseo.input-source-auto-converter", "com.protoseo.input-source-auto-converter",
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for // "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
"com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
"com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch
"com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
) )
suspend fun main() { suspend fun main() {
@@ -50,15 +42,19 @@ suspend fun main() {
parameter("dependency", "IdeaVIM") parameter("dependency", "IdeaVIM")
parameter("includeOptional", true) parameter("includeOptional", true)
} }
val output = response.body<List<String>>().toSet() val output = response.body<List<String>>()
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()
) )
} }

View File

@@ -8,12 +8,6 @@
package scripts.release package scripts.release
import org.eclipse.jgit.lib.ObjectId
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.revwalk.RevWalk
import org.eclipse.jgit.revwalk.filter.RevFilter
fun main(args: Array<String>) { fun main(args: Array<String>) {
println("HI!") println("HI!")
val projectDir = args[0] val projectDir = args[0]
@@ -25,12 +19,10 @@ fun main(args: Array<String>) {
check(branch == "master") { check(branch == "master") {
"We should be on master branch" "We should be on master branch"
} }
val mergeBaseCommit = getMergeBaseWithMaster(projectDir, objectId)
println("Base commit $mergeBaseCommit")
withGit(projectDir) { git -> withGit(projectDir) { git ->
val log = git.log().setMaxCount(500).call().toList() val log = git.log().setMaxCount(500).call().toList()
println("First commit hash in log: " + log.first().name + " log size: ${log.size}") println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
val logDiff = log.takeWhile { it.id.name != mergeBaseCommit } val logDiff = log.takeWhile { it.id.name != objectId.name }
val numCommits = logDiff.size val numCommits = logDiff.size
println("Log diff size is $numCommits") println("Log diff size is $numCommits")
check(numCommits < 450) { check(numCommits < 450) {
@@ -43,18 +35,3 @@ fun main(args: Array<String>) {
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']") println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
} }
} }
private fun getMergeBaseWithMaster(projectDir: String, tag: ObjectId): String {
withRepo(projectDir) { repo ->
val master = repo.resolve("master")
RevWalk(repo).use { walk ->
val tagRevCommit = walk.parseCommit(tag)
val masterRevCommit = walk.parseCommit(master)
walk.setRevFilter(RevFilter.MERGE_BASE)
walk.markStart(tagRevCommit)
walk.markStart(masterRevCommit)
val mergeBase: RevCommit = walk.next()
return mergeBase.name
}
}
}

View File

@@ -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()
}

View File

@@ -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)

View 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
View 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'

View File

@@ -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")

View File

@@ -64,7 +64,7 @@ finallyBlock: (WS | COLON)* FINALLY WS* ((inline_comment NEW_LINE) | (
; ;
functionDefinition: functionDefinition:
(WS | COLON)* FUNCTION (replace = BANG)? WS+ (SID | SNR)? (anyCaseNameWithDigitsAndUnderscores NUM)* (functionScope COLON)? (functionName | (literalDictionaryKey (DOT literalDictionaryKey)+)) WS* L_PAREN WS* argumentsDeclaration WS* R_PAREN WS* (functionFlag WS*)* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)+) (WS | COLON)* FUNCTION (replace = EXCLAMATION)? WS+ (SID | SNR)? (anyCaseNameWithDigitsAndUnderscores NUM)* (functionScope COLON)? (functionName | (literalDictionaryKey (DOT literalDictionaryKey)+)) WS* L_PAREN WS* argumentsDeclaration WS* R_PAREN WS* (functionFlag WS*)* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)+)
blockMember* blockMember*
(WS | COLON)* ENDFUNCTION WS* ((inline_comment NEW_LINE) | (NEW_LINE | BAR)) (WS | COLON)* ENDFUNCTION WS* ((inline_comment NEW_LINE) | (NEW_LINE | BAR))
; ;
@@ -92,7 +92,7 @@ command:
(WS | COLON)* range? (WS | COLON)* ECHO (WS* expr)* WS* (NEW_LINE | BAR)+ (WS | COLON)* range? (WS | COLON)* ECHO (WS* expr)* WS* (NEW_LINE | BAR)+
#EchoCommand| #EchoCommand|
(WS | COLON)* range? (WS | COLON)* DELF (replace = BANG)? WS+ (functionScope COLON)? functionName ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+) (WS | COLON)* range? (WS | COLON)* DELF (replace = EXCLAMATION)? WS+ (functionScope COLON)? functionName ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
#DelfunctionCommand| #DelfunctionCommand|
(WS | COLON)* range? (WS | COLON)* CALL WS+ expr WS* ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+) (WS | COLON)* range? (WS | COLON)* CALL WS+ expr WS* ((inline_comment NEW_LINE+) | (NEW_LINE | BAR)+)
@@ -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,12 +115,10 @@ 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
) )
bangModifier = BANG?
WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR)) (NEW_LINE | BAR)* WS* ((commandArgumentWithoutBars? inline_comment NEW_LINE) | (commandArgumentWithoutBars? NEW_LINE) | (commandArgumentWithoutBars? BAR)) (NEW_LINE | BAR)*
#CommandWithComment| #CommandWithComment|
@@ -132,19 +126,17 @@ command:
name = ( name = (
MAP | MAP_CLEAR | UNMAP | SORT | REGISTERS | CMD | H_LOWERCASE | HELP | NORMAL MAP | MAP_CLEAR | UNMAP | SORT | REGISTERS | CMD | H_LOWERCASE | HELP | NORMAL
) )
bangModifier = BANG?
WS* commandArgumentWithoutBars? (NEW_LINE | BAR)+ WS* commandArgumentWithoutBars? (NEW_LINE | BAR)+
#CommandWithoutComments| #CommandWithoutComments|
(WS | COLON)* range? (WS | COLON)* (WS | COLON)* range? (WS | COLON)*
name = ( name = (
G_LOWERCASE | GLOBAL | V_LOWERCASE | V_GLOBAL | S_LOWERCASE | SUBSTITUTE | TILDE | AMPERSAND | BANG G_LOWERCASE | GLOBAL | V_LOWERCASE | V_GLOBAL | S_LOWERCASE | SUBSTITUTE | TILDE | AMPERSAND | EXCLAMATION
) )
bangModifier = BANG?
WS* commandArgumentWithBars? NEW_LINE+ WS* commandArgumentWithBars? NEW_LINE+
#CommandWithBars| #CommandWithBars|
(WS | COLON)* range? (WS | COLON)* commandName (bangModifier = BANG?) WS* commandArgumentWithBars? (NEW_LINE | BAR)+ (WS | COLON)* range? (WS | COLON)* commandName WS* commandArgumentWithBars? (NEW_LINE | BAR)+
#OtherCommand #OtherCommand
; ;
commandArgumentWithBars: ~(NEW_LINE)+; commandArgumentWithBars: ~(NEW_LINE)+;
@@ -211,7 +203,7 @@ commandName:
(LESS)+ (LESS)+
| (GREATER)+ | (GREATER)+
| anyCaseNameWithDigitsAndUnderscoresExceptKeywords | anyCaseNameWithDigitsAndUnderscoresExceptKeywords
| commandName (bang = BANG) | commandName EXCLAMATION
; ;
@@ -223,7 +215,7 @@ commandName:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
expr: expr:
expr L_BRACKET expr R_BRACKET #OneElementSublistExpression expr L_BRACKET expr R_BRACKET #OneElementSublistExpression
| WS* BANG WS* expr #UnaryExpression | WS* EXCLAMATION WS* expr #UnaryExpression
| expr L_BRACKET WS* from = expr? WS* COLON WS* to = expr? WS* R_BRACKET #SublistExpression | expr L_BRACKET WS* from = expr? WS* COLON WS* to = expr? WS* R_BRACKET #SublistExpression
| expr WS* binaryOperator1 WS* expr #BinExpression1 | expr WS* binaryOperator1 WS* expr #BinExpression1
| expr WS* binaryOperator2 WS* expr #BinExpression2 | expr WS* binaryOperator2 WS* expr #BinExpression2
@@ -622,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';
@@ -692,7 +683,6 @@ MAP_CLEAR: ('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc
| (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lea') | (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lea')
| (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lear'); | (('mapc' | 'nmapc' | 'vmapc' | 'xmapc' | 'smapc' | 'omapc' | 'imapc' | 'cmapc') 'lear');
MAP: 'map' MAP: 'map'
| 'smap'
| 'nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm' | 'nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm'
| (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'a') | (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'a')
| (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'ap') | (('nm' | 'vm' | 'xm' | 'om' | 'im' | 'cm') 'ap')
@@ -702,8 +692,7 @@ MAP: 'map'
| (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 're') | (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 're')
| (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rem') | (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rem')
| (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rema') | (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'rema')
| (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'remap') | (('no' | 'nno' | 'vno' | 'xno' | 'ono' | 'ino' | 'cno') 'remap');
| ('snor' | 'snore' | 'snorem' | 'snorema' | 'snoremap');
UNMAP: 'unm' | 'nun' | 'vu' | 'xu' | 'sunm' | 'ou' | 'iu' | 'cu' UNMAP: 'unm' | 'nun' | 'vu' | 'xu' | 'sunm' | 'ou' | 'iu' | 'cu'
| 'vun' | 'xun' | 'oun' | 'iun' | 'cun' | 'vun' | 'xun' | 'oun' | 'iun' | 'cun'
| 'nunm' | 'vunm' | 'xunm' | 'ounm' | 'iunm' | 'cunm' | 'nunm' | 'vunm' | 'xunm' | 'ounm' | 'iunm' | 'cunm'
@@ -733,7 +722,7 @@ IDENTIFIER_LOWERCASE: [a-z]+;
IDENTIFIER_ANY_CASE: [a-zA-Z]+; IDENTIFIER_ANY_CASE: [a-zA-Z]+;
// Unary operators // Unary operators
BANG: '!'; EXCLAMATION: '!';
// Punctuation // Punctuation
L_PAREN: '('; L_PAREN: '(';
@@ -835,4 +824,4 @@ AUGROUP_SKIP: NEW_LINE (WS|COLON)* AUGROUP .*? AUGROUP WS+ END -> skip
// All the other symbols // All the other symbols
UNICODE_CHAR: '\u0000'..'\uFFFE'; UNICODE_CHAR: '\u0000'..'\uFFFE';
EMOJI: [\p{Emoji}]; EMOJI: [\p{Emoji}];

View File

@@ -11,6 +11,7 @@ package com.maddyhome.idea.vim;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.actionSystem.TypedAction; import com.intellij.openapi.editor.actionSystem.TypedAction;
@@ -79,6 +80,14 @@ public class EventFacade {
action.unregisterCustomShortcutSet(component); action.unregisterCustomShortcutSet(component);
} }
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
document.addDocumentListener(listener);
}
public void removeDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
document.removeDocumentListener(listener);
}
public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) { public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable); EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
} }
@@ -89,12 +98,20 @@ public class EventFacade {
editor.getCaretModel().addCaretListener(listener, disposable); editor.getCaretModel().addCaretListener(listener, disposable);
} }
public void removeCaretListener(@NotNull Editor editor, @NotNull CaretListener listener) {
editor.getCaretModel().removeCaretListener(listener);
}
public void addEditorMouseListener(@NotNull Editor editor, public void addEditorMouseListener(@NotNull Editor editor,
@NotNull EditorMouseListener listener, @NotNull EditorMouseListener listener,
@NotNull Disposable disposable) { @NotNull Disposable disposable) {
editor.addEditorMouseListener(listener, disposable); editor.addEditorMouseListener(listener, disposable);
} }
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
editor.removeEditorMouseListener(listener);
}
public void addComponentMouseListener(@NotNull Component component, public void addComponentMouseListener(@NotNull Component component,
@NotNull MouseListener mouseListener, @NotNull MouseListener mouseListener,
@NotNull Disposable disposable) { @NotNull Disposable disposable) {
@@ -102,18 +119,30 @@ public class EventFacade {
Disposer.register(disposable, () -> component.removeMouseListener(mouseListener)); Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
} }
public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
component.removeMouseListener(mouseListener);
}
public void addEditorMouseMotionListener(@NotNull Editor editor, public void addEditorMouseMotionListener(@NotNull Editor editor,
@NotNull EditorMouseMotionListener listener, @NotNull EditorMouseMotionListener listener,
@NotNull Disposable disposable) { @NotNull Disposable disposable) {
editor.addEditorMouseMotionListener(listener, disposable); editor.addEditorMouseMotionListener(listener, disposable);
} }
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
editor.removeEditorMouseMotionListener(listener);
}
public void addEditorSelectionListener(@NotNull Editor editor, public void addEditorSelectionListener(@NotNull Editor editor,
@NotNull SelectionListener listener, @NotNull SelectionListener listener,
@NotNull Disposable disposable) { @NotNull Disposable disposable) {
editor.getSelectionModel().addSelectionListener(listener, disposable); editor.getSelectionModel().addSelectionListener(listener, disposable);
} }
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
editor.getSelectionModel().removeSelectionListener(listener);
}
private @NotNull TypedAction getTypedAction() { private @NotNull TypedAction getTypedAction() {
return TypedAction.getInstance(); return TypedAction.getInstance();
} }

View File

@@ -8,23 +8,14 @@
package com.maddyhome.idea.vim package com.maddyhome.idea.vim
import com.intellij.ide.BrowserUtil
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginStateListener
import com.intellij.ide.plugins.PluginStateManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManagerListener import com.intellij.openapi.project.ProjectManagerListener
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.updateSettings.impl.UpdateSettings
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.helper.localEditors
import com.maddyhome.idea.vim.newapi.globalIjOptions import com.maddyhome.idea.vim.newapi.globalIjOptions
import com.maddyhome.idea.vim.newapi.initInjector
import com.maddyhome.idea.vim.ui.JoinEap
import com.maddyhome.idea.vim.ui.JoinEap.EAP_LINK
/** /**
* @author Alex Plate * @author Alex Plate
@@ -33,43 +24,20 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
private var firstInitializationOccurred = false private var firstInitializationOccurred = false
// TODO
// We should migrate to some solution from https://plugins.jetbrains.com/docs/intellij/plugin-components.html#application-startup
// If you'd like to add a new code here, please consider using one of the things described there.
override suspend fun execute(project: Project) { override suspend fun execute(project: Project) {
if (firstInitializationOccurred) return if (firstInitializationOccurred) return
firstInitializationOccurred = true firstInitializationOccurred = true
if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
}
// This code should be executed once // This code should be executed once
VimPlugin.getInstance().initialize() VimPlugin.getInstance().initialize()
// Uninstall survey. Should be registered once for all projects
PluginStateManager.addStateListener(object : PluginStateListener {
override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
}
override fun uninstall(descriptor: IdeaPluginDescriptor) {
if (descriptor.pluginId == VimPlugin.getPluginId()) {
BrowserUtil.open("https://surveys.jetbrains.com/s3/ideavim-uninstall-feedback")
}
}
})
} }
} }
// This is a temporal workaround for VIM-2487 // This is a temporal workaround for VIM-2487
internal class PyNotebooksCloseWorkaround : ProjectManagerListener { internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
override fun projectClosingBeforeSave(project: Project) { override fun projectClosingBeforeSave(project: Project) {
initInjector()
// TODO: Confirm context in CWM scenario
if (injector.globalIjOptions().closenotebooks) { if (injector.globalIjOptions().closenotebooks) {
injector.editorGroup.getEditors().forEach { vimEditor -> localEditors().forEach { editor ->
val editor = (vimEditor as IjVimEditor).editor
val virtualFile = EditorHelper.getVirtualFile(editor) val virtualFile = EditorHelper.getVirtualFile(editor)
if (virtualFile?.extension == "ipynb") { if (virtualFile?.extension == "ipynb") {
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project) val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)

View File

@@ -14,43 +14,44 @@ 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() registerEmptyShortcuts() // todo most likely it is not needed
} }
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 registerEmptyShortcuts() {
val parser = VimPlugin.getKey() val parser = VimPlugin.getKey()
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we // The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
// still need to register the shortcut, to make sure the editor doesn't swallow it. // still need to register the shortcut, to make sure the editor doesn't swallow it.
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System) parser
.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
} }
} }

View File

@@ -10,7 +10,6 @@ package com.maddyhome.idea.vim;
import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.Application; import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.PersistentStateComponent;
@@ -25,8 +24,10 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.SlowOperations; import com.maddyhome.idea.vim.api.VimEditor;
import com.maddyhome.idea.vim.api.*; import com.maddyhome.idea.vim.api.VimInjectorKt;
import com.maddyhome.idea.vim.api.VimKeyGroup;
import com.maddyhome.idea.vim.api.VimOptionGroup;
import com.maddyhome.idea.vim.config.VimState; import com.maddyhome.idea.vim.config.VimState;
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator; import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar; import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
@@ -35,9 +36,9 @@ import com.maddyhome.idea.vim.group.copy.PutGroup;
import com.maddyhome.idea.vim.group.visual.VisualMotionGroup; 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.IjVimInjectorKt; 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;
@@ -45,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;
@@ -66,7 +66,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
private static final Logger LOG = Logger.getInstance(VimPlugin.class); private static final Logger LOG = Logger.getInstance(VimPlugin.class);
static { static {
IjVimInjectorKt.initInjector(); VimInjectorKt.setInjector(new IjVimInjector());
} }
private final @NotNull VimState state = new VimState(); private final @NotNull VimState state = new VimState();
@@ -123,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() {
@@ -139,8 +139,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
return (MacroGroup)VimInjectorKt.getInjector().getMacro(); return (MacroGroup)VimInjectorKt.getInjector().getMacro();
} }
public static @NotNull VimDigraphGroup getDigraph() { public static @NotNull DigraphGroup getDigraph() {
return VimInjectorKt.getInjector().getDigraphGroup(); return (DigraphGroup)VimInjectorKt.getInjector().getDigraphGroup();
} }
public static @NotNull HistoryGroup getHistory() { public static @NotNull HistoryGroup getHistory() {
@@ -211,22 +211,22 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
public static void setEnabled(final boolean enabled) { public static void setEnabled(final boolean enabled) {
if (isEnabled() == enabled) return; if (isEnabled() == enabled) return;
if (!enabled) {
getInstance().turnOffPlugin(true);
}
getInstance().enabled = enabled; getInstance().enabled = enabled;
if (enabled) {
getInstance().turnOnPlugin();
}
if (enabled) { if (enabled) {
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn(); VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
} else { } else {
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff(); VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
} }
if (!enabled) {
getInstance().turnOffPlugin(true);
}
if (enabled) {
getInstance().turnOnPlugin();
}
StatusBarIconFactory.Util.INSTANCE.updateIcon(); StatusBarIconFactory.Util.INSTANCE.updateIcon();
} }
@@ -282,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();
}
} }
} }
@@ -337,9 +331,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
// 4) ~/.ideavimrc execution // 4) ~/.ideavimrc execution
// Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window // Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window
try (AccessToken ignore = SlowOperations.knownIssue("VIM-3661")) { registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
}
// Turing on should be performed after all commands registration // Turing on should be performed after all commands registration
getSearch().turnOn(); getSearch().turnOn();
@@ -347,21 +339,20 @@ 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();
if (onOffDisposable != null) { if (onOffDisposable != null) {
Disposer.dispose(onOffDisposable); Disposer.dispose(onOffDisposable);
onOffDisposable = null;
} }
} }

View File

@@ -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
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().setEditor(null) EditorHolderService.getInstance().editor = null
} }
companion object { companion object {

View File

@@ -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))
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")

View File

@@ -1,29 +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
package com.maddyhome.idea.vim.action.ex
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.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
@CommandOrMotion(keys = ["<C-B>", "<Home>"], modes = [Mode.CMD_LINE]) @CommandOrMotion(keys = [":"], modes = [Mode.NORMAL, Mode.VISUAL, Mode.OP_PENDING])
class MoveCaretToLineStart : VimActionHandler.SingleExecution() { internal class ExEntryAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED 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 {
val commandLine = injector.commandLine.getActiveCommandLine() ?: return false VimPlugin.getProcess().startExCommand(editor, context, cmd)
commandLine.caret.offset = 0
return true return true
} }
} }

View File

@@ -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"
} }

View File

@@ -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
}
}

View File

@@ -1,52 +0,0 @@
package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.command.UndoConfirmationPolicy
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
import com.intellij.openapi.project.DumbAwareAction
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode
class VimRunLastMacroInOpenFiles : DumbAwareAction() {
override fun update(e: AnActionEvent) {
val lastRegister = injector.macro.lastRegister
val isEnabled = lastRegister != 0.toChar()
e.presentation.isEnabled = isEnabled
e.presentation.text = if (isEnabled) "Run Macro '${lastRegister}' in Open Files" else "Run Last Macro in Open Files"
}
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.EDT
}
override fun actionPerformed(e: AnActionEvent) {
val project = e.project ?: return
val fileEditorManager = FileEditorManagerEx.getInstanceExIfCreated(project) ?: return
val editors = fileEditorManager.allEditors.filterIsInstance<TextEditor>()
WriteCommandAction.writeCommandAction(project)
.withName(e.presentation.text)
.withGlobalUndo()
.withUndoConfirmationPolicy(UndoConfirmationPolicy.REQUEST_CONFIRMATION)
.run<RuntimeException> {
val reg = injector.macro.lastRegister
for (editor in editors) {
fileEditorManager.openFile(editor.file, true)
val vimEditor = editor.editor.vim
vimEditor.mode = Mode.NORMAL()
KeyHandler.getInstance().reset(vimEditor)
injector.macro.playbackRegister(vimEditor, IjEditorExecutionContext(e.dataContext), reg, 1)
}
}
}
}

View File

@@ -43,10 +43,8 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
import com.maddyhome.idea.vim.listener.AceJumpService 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.initInjector
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.state.mode.mode
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
@@ -61,14 +59,11 @@ 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*/ {
init {
initInjector()
}
private val traceTime: Boolean private val traceTime: Boolean
get() { get() {
// Make sure the injector is initialized
VimPlugin.getInstance()
return injector.globalOptions().ideatracetime return injector.globalOptions().ideatracetime
} }
@@ -84,8 +79,11 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
// Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler? // Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
try { try {
val start = if (traceTime) System.currentTimeMillis() else null val start = if (traceTime) System.currentTimeMillis() else null
val keyHandler = KeyHandler.getInstance() KeyHandler.getInstance().handleKey(
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState) editor.vim,
keyStroke,
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
)
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")
@@ -258,14 +256,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().ijEditor
} 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:
@@ -298,10 +289,26 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
.addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)) .addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0))
.addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0)) .addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
.addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0)) .addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0)) .addAll(
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0)) getKeyStrokes(
KeyEvent.VK_LEFT,
0,
InputEvent.CTRL_DOWN_MASK,
InputEvent.SHIFT_DOWN_MASK,
InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
),
)
.addAll(
getKeyStrokes(
KeyEvent.VK_RIGHT,
0,
InputEvent.CTRL_DOWN_MASK,
InputEvent.SHIFT_DOWN_MASK,
InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
),
)
.addAll( .addAll(
getKeyStrokes( getKeyStrokes(
KeyEvent.VK_HOME, KeyEvent.VK_HOME,
@@ -373,10 +380,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>()

View File

@@ -14,7 +14,6 @@ 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.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Argument
@@ -22,76 +21,29 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.argumentCaptured import com.maddyhome.idea.vim.common.argumentCaptured
import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.visual.VimSelection 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.datatypes.VimFuncref
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
// todo make it multicaret // todo make it multicaret
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, motionType: SelectionType): Boolean { private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
val func = injector.globalOptions().operatorfunc val operatorFunction = injector.keyGroup.operatorFunction
if (func.isEmpty()) { if (operatorFunction == null) {
VimPlugin.showMessage(MessageHelper.message("E774")) VimPlugin.showMessage(MessageHelper.message("E774"))
return false return false
} }
val scriptContext = CommandLineVimLContext
// The option value is either a function name, which should have a handler, or it might be a lambda expression, or a
// `function` or `funcref` call expression, all of which will return a funcref (with a handler)
var handler = injector.functionService.getFunctionHandlerOrNull(null, func, scriptContext)
if (handler == null) {
val expression = injector.vimscriptParser.parseExpression(func)
if (expression != null) {
try {
val value = expression.evaluate(editor, context, scriptContext)
if (value is VimFuncref) {
handler = value.handler
}
} catch (_: ExException) {
// Get the argument for function('...') or funcref('...') for the error message
val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
}
else {
func
}
VimPlugin.showMessage("E117: Unknown function: $functionName")
return false
}
}
}
if (handler == null) {
VimPlugin.showMessage("E117: Unknown function: $func")
return false
}
val arg = when (motionType) {
SelectionType.LINE_WISE -> "line"
SelectionType.CHARACTER_WISE -> "char"
SelectionType.BLOCK_WISE -> "block"
}
val saveRepeatHandler = VimRepeater.repeatHandler val saveRepeatHandler = VimRepeater.repeatHandler
injector.markService.setChangeMarks(editor.primaryCaret(), textRange) injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
KeyHandler.getInstance().reset(editor) KeyHandler.getInstance().reset(editor)
val result = operatorFunction.apply(editor, context, selectionType)
val arguments = listOf(SimpleExpression(arg))
handler.executeFunction(arguments, editor, context, scriptContext)
VimRepeater.repeatHandler = saveRepeatHandler VimRepeater.repeatHandler = saveRepeatHandler
return true return result
} }
@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL]) @CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])
@@ -101,13 +53,19 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
override val argumentType: Argument.Type = Argument.Type.MOTION override val argumentType: Argument.Type = Argument.Type.MOTION
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 as? Argument.Motion ?: 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)
if (range != null) { if (range != null) {
return doOperatorAction(editor, context, range, argument.getMotionType()) val selectionType = if (argument.motion.isLinewiseMotion()) {
SelectionType.LINE_WISE
} else {
SelectionType.CHARACTER_WISE
}
return doOperatorAction(editor, context, range, selectionType)
} }
return false return false
} }
@@ -115,7 +73,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
private fun getMotionRange( private fun getMotionRange(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
argument: Argument.Motion, argument: Argument,
operatorArguments: OperatorArguments, operatorArguments: OperatorArguments,
): TextRange? { ): TextRange? {
// Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start // Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
@@ -130,7 +88,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
operatorArguments, operatorArguments,
)?.normalize()?.let { )?.normalize()?.let {
// If we're linewise, make sure the end offset isn't just the EOL char // If we're linewise, make sure the end offset isn't just the EOL char
if (argument.getMotionType() == SelectionType.LINE_WISE && it.endOffset < editor.fileSize()) { if (argument.motion.isLinewiseMotion() && it.endOffset < editor.fileSize()) {
TextRange(it.startOffset, it.endOffset + 1) TextRange(it.startOffset, it.endOffset + 1)
} else { } else {
it it

View File

@@ -7,9 +7,9 @@
*/ */
package com.maddyhome.idea.vim.action.change package com.maddyhome.idea.vim.action.change
import com.intellij.openapi.command.CommandProcessor
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.intellij.openapi.command.CommandProcessor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor import com.maddyhome.idea.vim.api.VimEditor
@@ -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,8 +25,8 @@ 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
var lastCommand = VimRepeater.lastChangeCommand val lastCommand = VimRepeater.lastChangeCommand
if (lastCommand == null && Extension.lastExtensionHandler == null) return false if (lastCommand == null && Extension.lastExtensionHandler == null) return false
@@ -57,7 +58,12 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
) )
} else if (!repeatHandler && lastCommand != null) { } else if (!repeatHandler && lastCommand != null) {
if (cmd.rawCount > 0) { if (cmd.rawCount > 0) {
lastCommand = lastCommand.copy(rawCount = cmd.rawCount) lastCommand.count = cmd.count
val arg = lastCommand.argument
if (arg != null) {
val mot = arg.motion
mot.count = 0
}
} }
state.executingCommand = lastCommand state.executingCommand = lastCommand

View File

@@ -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,
@@ -40,7 +40,7 @@ class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecuti
): Boolean { ): Boolean {
injector.editorGroup.notifyIdeaJoin(editor) injector.editorGroup.notifyIdeaJoin(editor)
return injector.changeGroup.deleteJoinLines(editor, context, caret, operatorArguments.count1, false) return injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false, operatorArguments)
} }
override fun execute( override fun execute(

View File

@@ -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,8 +34,8 @@ 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, context, caret, operatorArguments.count1, true)) { if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
res = false res = false
} }
} }

View File

@@ -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,12 +39,11 @@ 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(
editor, editor,
context,
caret, caret,
range.toVimTextRange(true).normalize(), range.toVimTextRange(true).normalize(),
false, false,

View File

@@ -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,12 +39,11 @@ 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(
editor, editor,
context,
caret, caret,
range.toVimTextRange(true).normalize(), range.toVimTextRange(true).normalize(),
true, true,

View File

@@ -20,10 +20,13 @@ import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.IdeActionHandler import com.maddyhome.idea.vim.handler.IdeActionHandler
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
import java.util.* import java.util.*
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
override val type: Command.Type = Command.Type.DELETE
}
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT]) @CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) { internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
override val type: Command.Type = Command.Type.DELETE override val type: Command.Type = Command.Type.DELETE
@@ -33,23 +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
when (undo) {
is VimKeyBasedUndoService -> undo.setMergeUndoKey()
is VimTimestampBasedUndoService -> {
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])
@@ -62,23 +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
when (undo) {
is VimKeyBasedUndoService -> undo.setMergeUndoKey()
is VimTimestampBasedUndoService -> {
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])
@@ -86,7 +55,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
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 {
injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context) injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
return true return true
} }
} }

View File

@@ -1,33 +1,35 @@
/* /*
* 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.ex
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.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.*
import java.util.EnumSet
@CommandOrMotion(keys = ["<Insert>"], modes = [Mode.CMD_LINE]) /**
class ToggleInsertModeAction : VimActionHandler.SingleExecution() { * 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 type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_UNDO_AWARE)
override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX)
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 commandLine = injector.commandLine.getActiveCommandLine() ?: return false return VimPlugin.getProcess().processExEntry(editor, context)
commandLine.toggleReplaceMode()
return true
} }
} }

View File

@@ -9,48 +9,41 @@
package com.maddyhome.idea.vim.command package com.maddyhome.idea.vim.command
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.helper.vimStateMachine
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.VimStateMachine import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.state.mode.SelectionType
/** /**
* COMPATIBILITY-LAYER: Additional class * COMPATIBILITY-LAYER: Additional class
* Please see: https://jb.gg/zo8n0r * Please see: https://jb.gg/zo8n0r
*/ */
@Deprecated("Use `injector.vimState`") public class CommandState(private val machine: VimStateMachine) {
class CommandState(private val machine: VimStateMachine) {
val mode: Mode public val isOperatorPending: Boolean
get() = machine.isOperatorPending
public val mode: CommandState.Mode
get() { get() {
val myMode = machine.mode val myMode = machine.mode
return when (myMode) { return when (myMode) {
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL
} }
} }
@Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith( public val commandBuilder: CommandBuilder
"KeyHandler.getInstance().keyHandlerState.commandBuilder", get() = machine.commandBuilder
"com.maddyhome.idea.vim.KeyHandler"
)
)
val commandBuilder: CommandBuilder
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
@Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith( public val mappingState: MappingState
"KeyHandler.getInstance().keyHandlerState.mappingState", get() = machine.mappingState
"com.maddyhome.idea.vim.KeyHandler"
)
)
val mappingState: MappingState
get() = KeyHandler.getInstance().keyHandlerState.mappingState
enum class Mode { public enum class Mode {
// Basic modes // Basic modes
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/ COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
@@ -58,15 +51,22 @@ class CommandState(private val machine: VimStateMachine) {
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
} }
enum class SubMode { public enum class SubMode {
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
} }
companion object { public companion object {
@JvmStatic @JvmStatic
@Deprecated("Use `injector.vimState`") public fun getInstance(editor: Editor): CommandState {
fun getInstance(editor: Editor): CommandState { return CommandState(editor.vim.vimStateMachine)
return CommandState(injector.vimState)
} }
} }
} }
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
}

View File

@@ -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)
} }
} }

View File

@@ -9,20 +9,22 @@
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
import com.maddyhome.idea.vim.api.VimIndentConfig
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig { internal class IndentConfig private constructor(indentOptions: IndentOptions) {
private val indentSize = indentOptions.INDENT_SIZE private val indentSize = indentOptions.INDENT_SIZE
private val tabSize = indentOptions.TAB_SIZE private val tabSize = indentOptions.TAB_SIZE
private val isUseTabs = indentOptions.USE_TAB_CHARACTER private val isUseTabs = indentOptions.USE_TAB_CHARACTER
override fun getIndentSize(depth: Int): Int = indentSize * depth fun getTotalIndent(count: Int): Int = indentSize * count
override fun createIndentByDepth(depth: Int): String = createIndentBySize(getIndentSize(depth))
override fun createIndentBySize(size: Int): String { fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
fun createIndentBySize(size: Int): String {
val tabCount: Int val tabCount: Int
val spaceCount: Int val spaceCount: Int
if (isUseTabs) { if (isUseTabs) {
@@ -37,12 +39,13 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions): V
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 {

View File

@@ -18,8 +18,6 @@ import kotlin.reflect.KProperty
internal class VimState { internal class VimState {
var isIdeaJoinNotified by StateProperty("idea-join") var isIdeaJoinNotified by StateProperty("idea-join")
var isIdeaPutNotified by StateProperty("idea-put") var isIdeaPutNotified by StateProperty("idea-put")
var isNewUndoNotified by StateProperty("idea-undo")
var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
fun readData(element: Element) { fun readData(element: Element) {
val notifications = element.getChild("notifications") val notifications = element.getChild("notifications")

View File

@@ -9,142 +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.VimOutputPanelBase 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
import javax.swing.KeyStroke
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing /**
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() { * @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 fun scrollPage() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollPage()
}
override fun scrollHalfPage() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollHalfPage()
}
override fun scrollLine() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.scrollLine()
}
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()
}
}
override var label: String
get() {
val notNullEditor = editor ?: return ""
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return ""
return panel.myLabel.text
}
set(value) {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.myLabel.text = value
}
fun output(text: String) {
this.text = text this.text = text
}
fun clear() {
text = ""
}
override val atEnd: Boolean
get() {
val notNullEditor = editor ?: return false
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return false
return panel.isAtEnd()
}
override fun onBadKey() {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.onBadKey()
}
override fun close(key: KeyStroke?) {
val notNullEditor = editor ?: return
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
panel.close(key)
}
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
} }
} }

View File

@@ -27,30 +27,6 @@ public interface VimExtension {
return MappingOwner.Plugin.Companion.get(getName()); return MappingOwner.Plugin.Companion.get(getName());
} }
/**
* This method is always called AFTER the full execution of the `.ideavimrc` file.
* <p>
* During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
* This practically means that the .vimrc file is initialized first; then the plugins are loaded.
* See `:h initialization`
* <p>
* Why does this matter? Because this affects the order of commands are executed. For example,
* ```
* plug 'tommcdo/vim-exchange'
* let g:exchange_no_mappings=1
* ```
* Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
* immediately, this variable won't be initialized at the moment of plugin initialization.
* <p>
* There is also a tricky case for mappings override:
* ```
* plug 'tommcdo/vim-exchange'
* map X <Plug>(ExchangeLine)
* ```
* For this case, a plugin with a good implementation detects that there is already a defined mapping for
* `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
* to be defined before the plugin initialization.
*/
void init(); void init();
default void dispose() { default void dispose() {

View File

@@ -7,41 +7,28 @@
*/ */
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
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
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.ImmutableVimCaret import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode 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.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.vimStateMachine
import com.maddyhome.idea.vim.helper.noneOfEnum
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
import com.maddyhome.idea.vim.state.mode.SelectionType import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.ui.ModalEntry import com.maddyhome.idea.vim.ui.ModalEntry
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
import com.maddyhome.idea.vim.vimscript.model.Executable
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
import com.maddyhome.idea.vim.vimscript.model.VimLContext
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionFlag
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
/** /**
@@ -51,13 +38,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,
@@ -68,14 +55,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("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)", public fun putExtensionHandlerMapping(
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 +73,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 +85,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 +99,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 +110,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,
@@ -134,6 +120,12 @@ object VimExtensionFacade {
.setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler)) .setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
} }
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
@JvmStatic
public fun setOperatorFunction(function: OperatorFunction) {
VimPlugin.getKey().operatorFunction = function
}
/** /**
* Runs normal mode commands similar to ':normal! {commands}'. * Runs normal mode commands similar to ':normal! {commands}'.
* Mappings doesn't work with this function * Mappings doesn't work with this function
@@ -142,16 +134,15 @@ 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() keys.forEach { KeyHandler.getInstance().handleKey(editor.vim, it, context, false, false) }
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}")
@@ -161,7 +152,7 @@ object VimExtensionFacade {
LOG.trace("Unit test mode is active") LOG.trace("Unit test mode is active")
val mappingStack = KeyHandler.getInstance().keyStack val mappingStack = KeyHandler.getInstance().keyStack
mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also { mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
if (injector.registerGroup.isRecording) { if (editor.vim.vimStateMachine.isRecording) {
KeyHandler.getInstance().modalEntryKeys += it KeyHandler.getInstance().modalEntryKeys += it
} }
} }
@@ -182,107 +173,38 @@ 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 as ExEntryPanelService).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
@Deprecated("Please use com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister(com.maddyhome.idea.vim.api.VimEditor, char)") public fun getRegister(register: Char): List<KeyStroke>? {
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
} }
/** Get the current contents of the given register similar to 'getreg()'. */
@JvmStatic @JvmStatic
fun getRegister(editor: VimEditor, register: Char): List<KeyStroke>? { public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = VimPlugin.getRegister().getRegister(editor, injector.executionContextManager.getEditorExecutionContext(editor), register) ?: return null
return reg.keys
}
@JvmStatic
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
val reg = caret.registerStorage.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?>?) {
caret.registerStorage.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
fun exportScriptFunction(
scope: Scope?,
name: String,
args: List<String>,
defaultArgs: List<Pair<String, Expression>>,
hasOptionalArguments: Boolean,
flags: EnumSet<FunctionFlag>,
function: ScriptFunction
) {
var functionDeclaration: FunctionDeclaration? = null
val body = listOf(object : Executable {
// This context is set to the function declaration during initialisation and then set to the function execution
// context during execution
override lateinit var vimContext: VimLContext
override var rangeInScript: TextRange = TextRange(0, 0)
override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
return function.execute(editor, context, functionDeclaration!!.functionVariables)
}
})
functionDeclaration = FunctionDeclaration(
scope,
name,
args,
defaultArgs,
body,
replaceExisting = true,
flags,
hasOptionalArguments
)
functionDeclaration.rangeInScript = TextRange(0, 0)
body.forEach { it.vimContext = functionDeclaration }
injector.functionService.storeFunction(functionDeclaration)
}
}
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
editor, context, args ->
val type = args["type"]?.asString()
val selectionType = when (type) {
"line" -> SelectionType.LINE_WISE
"block" -> SelectionType.BLOCK_WISE
"char" -> SelectionType.CHARACTER_WISE
else -> return@exportScriptFunction ExecutionResult.Error
}
if (function.apply(editor, context, selectionType)) {
ExecutionResult.Success
}
else {
ExecutionResult.Error
}
}
}
fun interface ScriptFunction {
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
} }

View File

@@ -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
} }

View File

@@ -67,7 +67,7 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) { VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) {
if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) { if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) {
initExtension(extensionBean, name) initExtension(extensionBean, name)
PluginState.Util.enabledExtensions.add(name) PluginState.enabledExtensions.add(name)
} else { } else {
extensionBean.instance.dispose() extensionBean.instance.dispose()
} }
@@ -88,10 +88,29 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
} }
/** /**
* See the docs for [VimExtension.init] * During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
* This practically means that the .vimrc file is initialized first, then the plugins are loaded.
* See `:h initialization`
* *
* In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect * In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect
* the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished. * the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished.
*
* Why this matters? Because this affects the order of commands are executed. For example:
* ```
* plug 'tommcdo/vim-exchange'
* let g:exchange_no_mappings=1
* ```
* Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
* immediately, this variable won't be initialized at the moment of plugin initialization.
*
* There is also a tricky case for mappings override:
* ```
* plug 'tommcdo/vim-exchange'
* map X <Plug>(ExchangeLine)
* ```
* For this case, a plugin with a good implementation detects that there is already a defined mapping for
* `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
* to be defined before the plugin initialization.
*/ */
@JvmStatic @JvmStatic
fun enableDelayedExtensions() { fun enableDelayedExtensions() {

View File

@@ -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;
@@ -33,6 +32,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
@@ -63,8 +63,8 @@ public class VimArgTextObjExtension implements VimExtension {
*/ */
private static class BracketPairs { private static class BracketPairs {
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest). // NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
private final @NotNull String openBrackets; @NotNull private final String openBrackets;
private final @NotNull String closeBrackets; @NotNull private final String closeBrackets;
static class ParseException extends Exception { static class ParseException extends Exception {
public ParseException(@NotNull String message) { public ParseException(@NotNull String message) {
@@ -86,7 +86,8 @@ public class VimArgTextObjExtension implements VimExtension {
* @param bracketPairs comma-separated list of colon-separated bracket pairs. * @param bracketPairs comma-separated list of colon-separated bracket pairs.
* @throws ParseException if a syntax error is detected. * @throws ParseException if a syntax error is detected.
*/ */
static @NotNull BracketPairs fromBracketPairList(final @NotNull String bracketPairs) throws ParseException { @NotNull
static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseException {
StringBuilder openBrackets = new StringBuilder(); StringBuilder openBrackets = new StringBuilder();
StringBuilder closeBrackets = new StringBuilder(); StringBuilder closeBrackets = new StringBuilder();
ParseState state = ParseState.OPEN; ParseState state = ParseState.OPEN;
@@ -126,7 +127,7 @@ public class VimArgTextObjExtension implements VimExtension {
return new BracketPairs(openBrackets.toString(), closeBrackets.toString()); return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
} }
BracketPairs(final @NotNull String openBrackets, final @NotNull String closeBrackets) { BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) {
assert openBrackets.length() == closeBrackets.length(); assert openBrackets.length() == closeBrackets.length();
this.openBrackets = openBrackets; this.openBrackets = openBrackets;
this.closeBrackets = closeBrackets; this.closeBrackets = closeBrackets;
@@ -156,9 +157,10 @@ public class VimArgTextObjExtension implements VimExtension {
} }
} }
private static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")"); public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
private static @Nullable String bracketPairsVariable() { @Nullable
private static String bracketPairsVariable() {
final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs"); final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
if (value instanceof VimString vimValue) { if (value instanceof VimString vimValue) {
return vimValue.getValue(); return vimValue.getValue();
@@ -189,12 +191,13 @@ public class VimArgTextObjExtension implements VimExtension {
this.isInner = isInner; this.isInner = isInner;
} }
@Nullable
@Override @Override
public @Nullable TextRange getRange(@NotNull VimEditor editor, public TextRange getRange(@NotNull VimEditor editor,
@NotNull ImmutableVimCaret caret, @NotNull ImmutableVimCaret caret,
@NotNull ExecutionContext context, @NotNull ExecutionContext context,
int count, int count,
int rawCount) { int rawCount) {
BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS; BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
final String bracketPairsVar = bracketPairsVariable(); final String bracketPairsVar = bracketPairsVariable();
if (bracketPairsVar != null) { if (bracketPairsVar != null) {
@@ -232,25 +235,28 @@ public class VimArgTextObjExtension implements VimExtension {
return new TextRange(finder.getLeftBound(), finder.getRightBound()); return new TextRange(finder.getLeftBound(), finder.getRightBound());
} }
@NotNull
@Override @Override
public @NotNull TextObjectVisualType getVisualType() { public TextObjectVisualType getVisualType() {
return TextObjectVisualType.CHARACTER_WISE; return TextObjectVisualType.CHARACTER_WISE;
} }
} }
@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 KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
IjVimEditor vimEditor = (IjVimEditor) editor;
@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 (!(editor.getMode() instanceof Mode.OP_PENDING)) { if (!vimStateMachine.isOperatorPending()) {
int count0 = operatorArguments.getCount0();
editor.nativeCarets().forEach((VimCaret caret) -> { editor.nativeCarets().forEach((VimCaret caret) -> {
final TextRange range = textObjectHandler.getRange(editor, caret, context, Math.max(1, count0), count0); 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());
@@ -259,7 +265,8 @@ public class VimArgTextObjExtension implements VimExtension {
} }
}); });
} else { } else {
keyHandlerState.getCommandBuilder().addAction(textObjectHandler); vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
} }
} }
} }
@@ -269,9 +276,9 @@ public class VimArgTextObjExtension implements VimExtension {
* position * position
*/ */
private static class ArgBoundsFinder { private static class ArgBoundsFinder {
private final @NotNull CharSequence text; @NotNull private final CharSequence text;
private final @NotNull Document document; @NotNull private final Document document;
private final @NotNull BracketPairs brackets; @NotNull private final BracketPairs brackets;
private int leftBound = Integer.MAX_VALUE; private int leftBound = Integer.MAX_VALUE;
private int rightBound = Integer.MIN_VALUE; private int rightBound = Integer.MIN_VALUE;
private int leftBracket; private int leftBracket;
@@ -298,7 +305,7 @@ public class VimArgTextObjExtension implements VimExtension {
* @param position starting position. * @param position starting position.
*/ */
boolean findBoundsAt(int position) throws IllegalStateException { boolean findBoundsAt(int position) throws IllegalStateException {
if (text.isEmpty()) { if (text.length() == 0) {
error = "empty document"; error = "empty document";
return false; return false;
} }

View File

@@ -18,55 +18,57 @@ 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
import com.maddyhome.idea.vim.api.getLineEndOffset import com.maddyhome.idea.vim.api.getLineEndOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.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.addCommand import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.exportOperatorFunction import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.handler.TextObjectActionHandler import com.maddyhome.idea.vim.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
import com.maddyhome.idea.vim.newapi.vim import com.maddyhome.idea.vim.newapi.vim
import com.maddyhome.idea.vim.state.mode.Mode import java.util.*
import com.maddyhome.idea.vim.state.mode.SelectionType
internal class CommentaryExtension : VimExtension { internal class CommentaryExtension : VimExtension {
object Util { companion object {
fun doCommentary( fun doCommentary(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
range: TextRange, range: TextRange,
selectionType: SelectionType, selectionType: SelectionType,
resetCaret: Boolean = true, resetCaret: Boolean,
): 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)
} }
return runWriteAction { return runWriteAction {
// Treat block- and character-wise selections as block comments. Fall back if the first action isn't available // Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
// isn't available
val actions = if (selectionType === SelectionType.LINE_WISE) { val actions = if (selectionType === SelectionType.LINE_WISE) {
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK) listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
} else { } else {
@@ -75,12 +77,11 @@ internal class CommentaryExtension : VimExtension {
val project = editor.ij.project!! val project = editor.ij.project!!
val callback = { afterCommenting(mode, editor, resetCaret, range) } val callback = { afterCommenting(mode, editor, resetCaret, range) }
actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) } actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
} }
} }
private fun executeActionWithCallbackOnSuccess( private fun executeActionWithCallbackOnSuccess(
editor: VimEditor,
action: String, action: String,
project: Project, project: Project,
context: ExecutionContext, context: ExecutionContext,
@@ -89,7 +90,7 @@ internal class CommentaryExtension : VimExtension {
val res = Ref.create<Boolean>(false) val res = Ref.create<Boolean>(false)
AsyncActionExecutionService.getInstance(project).withExecutionAfterAction( AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
action, action,
{ res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) }, { res.set(injector.actionExecutor.executeAction(action, context)) },
{ if (res.get()) callback() }) { if (res.get()) callback() })
return res.get() return res.get()
} }
@@ -112,17 +113,12 @@ internal class CommentaryExtension : VimExtension {
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column, // first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept // then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
// the difference // the difference
// TODO: If we don't move the caret to the start offset, we should maintain the current logical position
if (resetCaret) { if (resetCaret) {
editor.primaryCaret().moveToOffset(range.startOffset) editor.primaryCaret().moveToOffset(range.startOffset)
} }
} }
} }
companion object {
private const val OPERATOR_FUNC = "CommentaryOperatorFunc"
}
override fun getName() = "commentary" override fun getName() = "commentary"
override fun init() { override fun init() {
@@ -149,16 +145,6 @@ internal class CommentaryExtension : VimExtension {
putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true) putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
addCommand("Commentary", CommentaryCommandAliasHandler()) addCommand("Commentary", CommentaryCommandAliasHandler())
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, CommentaryOperatorFunction())
}
private class CommentaryOperatorFunction : OperatorFunction {
// todo make it multicaret
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
return Util.doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
}
} }
/** /**
@@ -167,21 +153,29 @@ internal class CommentaryExtension : VimExtension {
* E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to * E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
* invoke the operator. This object is both the mapping handler and the operator function. * invoke the operator. This object is both the mapping handler and the operator function.
*/ */
private class CommentaryOperatorHandler : ExtensionHandler { private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
override val isRepeatable = true override val isRepeatable = true
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
injector.globalOptions().operatorfunc = OPERATOR_FUNC setOperatorFunction(this)
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
} }
// todo make it multicaret
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
}
} }
private class CommentaryMappingHandler : ExtensionHandler { private class CommentaryMappingHandler : ExtensionHandler {
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 keyState = KeyHandler.getInstance().keyHandlerState val commandState = editor.vimStateMachine
keyState.commandBuilder.addAction(CommentaryTextObjectMotionHandler)
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
commandState.commandBuilder.completeCommandPart(Argument(command))
} }
} }
@@ -244,14 +238,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( doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
editor,
context,
range.getLineRange(editor, editor.primaryCaret()).toTextRange(editor),
SelectionType.LINE_WISE,
false
)
} }
} }
} }

View File

@@ -19,22 +19,24 @@ import com.intellij.openapi.util.Key
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.getOffset import com.maddyhome.idea.vim.api.getOffset
import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.api.setChangeMarks import com.maddyhome.idea.vim.api.setChangeMarks
import com.maddyhome.idea.vim.command.MappingMode import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.command.OperatorArguments import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
import com.maddyhome.idea.vim.state.mode.selectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
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.executeNormalWithoutMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.state.mode.mode
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
@@ -43,8 +45,6 @@ import com.maddyhome.idea.vim.mark.VimMarkConstants
import com.maddyhome.idea.vim.newapi.IjVimEditor import com.maddyhome.idea.vim.newapi.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.mode.SelectionType
import com.maddyhome.idea.vim.state.mode.selectionType
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
/** /**
@@ -72,13 +72,30 @@ internal class VimExchangeExtension : VimExtension {
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true) putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true) putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
} }
object Util { companion object {
@NonNls
const val EXCHANGE_CMD = "<Plug>(Exchange)"
@NonNls
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
@NonNls
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
val EXCHANGE_KEY = Key<Exchange>("exchange") val EXCHANGE_KEY = Key<Exchange>("exchange")
// End mark has always greater of eq offset than start mark
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
private var myHighlighter: RangeHighlighter? = null
fun setHighlighter(highlighter: RangeHighlighter) {
myHighlighter = highlighter
}
fun getHighlighter(): RangeHighlighter? = myHighlighter
}
fun clearExchange(editor: Editor) { fun clearExchange(editor: Editor) {
editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let { editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
editor.markupModel.removeHighlighter(it) editor.markupModel.removeHighlighter(it)
@@ -87,25 +104,18 @@ internal class VimExchangeExtension : VimExtension {
} }
} }
companion object {
@NonNls private const val EXCHANGE_CMD = "<Plug>(Exchange)"
@NonNls private const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
@NonNls private const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
@NonNls private const val OPERATOR_FUNC = "ExchangeOperatorFunc"
}
private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler { private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler {
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) {
injector.globalOptions().operatorfunc = OPERATOR_FUNC setOperatorFunction(Operator(false))
executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
} }
} }
private class ExchangeClearHandler : ExtensionHandler { private class ExchangeClearHandler : ExtensionHandler {
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
Util.clearExchange(editor.ij) clearExchange(editor.ij)
} }
} }
@@ -115,12 +125,12 @@ internal class VimExchangeExtension : VimExtension {
val mode = editor.mode val mode = editor.mode
// Leave visual mode to create selection marks // Leave visual mode to create selection marks
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij) executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
Operator(true).apply(editor, context, mode.selectionType ?: SelectionType.CHARACTER_WISE) Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
} }
} }
} }
private class Operator(private val isVisual: Boolean = false) : OperatorFunction { private class Operator(private val isVisual: Boolean) : OperatorFunction {
fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col) fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
fun SelectionType.getString() = when (this) { fun SelectionType.getString() = when (this) {
SelectionType.CHARACTER_WISE -> "v" SelectionType.CHARACTER_WISE -> "v"
@@ -138,7 +148,7 @@ internal class VimExchangeExtension : VimExtension {
else -> HighlighterTargetArea.EXACT_RANGE else -> HighlighterTargetArea.EXACT_RANGE
} }
val isVisualLine = ex.type == SelectionType.LINE_WISE val isVisualLine = ex.type == SelectionType.LINE_WISE
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || isVisual)) 1 else 0 val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0
return ijEditor.markupModel.addRangeHighlighter( return ijEditor.markupModel.addRangeHighlighter(
ijEditor.getMarkOffset(ex.start), ijEditor.getMarkOffset(ex.start),
(ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize), (ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
@@ -148,12 +158,12 @@ internal class VimExchangeExtension : VimExtension {
) )
} }
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE) val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
val exchange1 = ijEditor.getUserData(Util.EXCHANGE_KEY) val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
if (exchange1 == null) { if (exchange1 == null) {
val highlighter = highlightExchange(currentExchange) val highlighter = highlightExchange(currentExchange)
currentExchange.setHighlighter(highlighter) currentExchange.setHighlighter(highlighter)
ijEditor.putUserData(Util.EXCHANGE_KEY, currentExchange) ijEditor.putUserData(EXCHANGE_KEY, currentExchange)
return true return true
} else { } else {
val cmp = compareExchanges(exchange1, currentExchange) val cmp = compareExchanges(exchange1, currentExchange)
@@ -179,7 +189,7 @@ internal class VimExchangeExtension : VimExtension {
} }
} }
exchange(ijEditor, ex1, ex2, reverse, expand) exchange(ijEditor, ex1, ex2, reverse, expand)
Util.clearExchange(ijEditor) clearExchange(ijEditor)
return true return true
} }
} }
@@ -222,10 +232,10 @@ internal class VimExchangeExtension : VimExtension {
} }
} }
val zRegText = getRegister(editor.vim, 'z') val zRegText = getRegister('z')
val unnRegText = getRegister(editor.vim, '"') val unnRegText = getRegister('"')
val startRegText = getRegister(editor.vim, '*') val startRegText = getRegister('*')
val plusRegText = getRegister(editor.vim, '+') val plusRegText = getRegister('+')
runWriteAction { runWriteAction {
// TODO handle: // TODO handle:
// " Compare using =~ because "'==' != 0" returns 0 // " Compare using =~ because "'==' != 0" returns 0
@@ -299,7 +309,7 @@ internal class VimExchangeExtension : VimExtension {
private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange { private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
// TODO: improve KeyStroke list to sting conversion // TODO: improve KeyStroke list to sting conversion
fun getRegisterText(reg: Char): String = getRegister(editor.vim, reg)?.map { it.keyChar }?.joinToString("") ?: "" fun getRegisterText(reg: Char): String = getRegister(reg)?.map { it.keyChar }?.joinToString("") ?: ""
fun getMarks(isVisual: Boolean): Pair<Mark, Mark> { fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
val (startMark, endMark) = val (startMark, endMark) =
if (isVisual) { if (isVisual) {
@@ -313,9 +323,9 @@ internal class VimExchangeExtension : VimExtension {
return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!) return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!)
} }
val unnRegText = getRegister(editor.vim, '"') val unnRegText = getRegister('"')
val starRegText = getRegister(editor.vim, '*') val starRegText = getRegister('*')
val plusRegText = getRegister(editor.vim, '+') val plusRegText = getRegister('+')
val (selectionStart, selectionEnd) = getMarks(isVisual) val (selectionStart, selectionEnd) = getMarks(isVisual)
if (isVisual) { if (isVisual) {
@@ -344,14 +354,4 @@ internal class VimExchangeExtension : VimExtension {
} }
} }
} }
// End mark has always greater of eq offset than start mark
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
private var myHighlighter: RangeHighlighter? = null
fun setHighlighter(highlighter: RangeHighlighter) {
myHighlighter = highlighter
}
fun getHighlighter(): RangeHighlighter? = myHighlighter
}
} }

Some files were not shown because too many files have changed in this diff Show More