mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-04-11 14:15:43 +02:00
Compare commits
267 Commits
f33589fbce
...
0937234558
Author | SHA1 | Date | |
---|---|---|---|
0937234558 | |||
4e1d7d4228 | |||
b5f7393e45 | |||
522fee294f | |||
300735a6da | |||
8d4f0d838f | |||
325a655dbf | |||
85da8f7b8d | |||
bd2e1e5888 | |||
34a1845cc2 | |||
d02cc5960d | |||
285053e530 | |||
f9ea683c9a | |||
d3f736a69e | |||
13bfc98ddf | |||
05fd29c0ec | |||
c8512a2e59 | |||
cc1437092f | |||
6d83464cd7 | |||
d5be9cc8b6 | |||
6026b304c2 | |||
![]() |
7c39e9e585 | ||
![]() |
60767fa908 | ||
![]() |
186feed8af | ||
![]() |
b3aaf75b48 | ||
![]() |
2b1b4fc71e | ||
![]() |
8a173c801c | ||
![]() |
38b5879d37 | ||
![]() |
d0900fd0c5 | ||
![]() |
79d2040fcf | ||
![]() |
2c83403cb0 | ||
![]() |
3f22c6f9e4 | ||
![]() |
abdb6cb906 | ||
![]() |
46f9aa4df2 | ||
![]() |
f40540d114 | ||
![]() |
79b9dbd656 | ||
![]() |
f8592a85f7 | ||
![]() |
5b8469c276 | ||
![]() |
0729e9623b | ||
![]() |
90b530e6db | ||
![]() |
dcaef64494 | ||
![]() |
b31f46d9f4 | ||
![]() |
bbab064281 | ||
![]() |
6f030eb8a6 | ||
![]() |
d52d631799 | ||
![]() |
84b485c6bc | ||
![]() |
3808bfea60 | ||
![]() |
3fa20dd417 | ||
![]() |
f76b07521e | ||
![]() |
55257e1017 | ||
![]() |
cccf2d9d2f | ||
![]() |
7826de6601 | ||
![]() |
33441f9ec3 | ||
![]() |
7d25d6d323 | ||
![]() |
fc93661c62 | ||
![]() |
098243fb12 | ||
![]() |
09321f409c | ||
![]() |
e899a99313 | ||
![]() |
393d2d4353 | ||
![]() |
e7d47fd7f3 | ||
![]() |
f17345e1ac | ||
![]() |
9ba0f5f47a | ||
![]() |
4a31e425e0 | ||
![]() |
017b36673b | ||
![]() |
156fbcd60d | ||
![]() |
cda85ae534 | ||
![]() |
2c7b966ec2 | ||
![]() |
cf49c0b76f | ||
![]() |
ce01318032 | ||
![]() |
c9b25cbb27 | ||
![]() |
3d1b502cd0 | ||
![]() |
572af0d349 | ||
![]() |
5770fbd70f | ||
![]() |
1af8d31272 | ||
![]() |
6eb9207ff6 | ||
![]() |
681cef1d69 | ||
![]() |
f88a1cbe06 | ||
![]() |
84df5451b3 | ||
![]() |
2c1cba96d2 | ||
![]() |
348dfa8af8 | ||
![]() |
34c8688dcf | ||
![]() |
6f193d8026 | ||
![]() |
8214202d43 | ||
![]() |
830a708489 | ||
![]() |
ee1928c836 | ||
![]() |
38102afc0f | ||
![]() |
8483e23ea7 | ||
![]() |
b3631b59ed | ||
![]() |
dbc626c794 | ||
![]() |
692e4434d1 | ||
![]() |
a836b31ebe | ||
![]() |
89e530276f | ||
![]() |
0b817ba575 | ||
![]() |
c2513c8a74 | ||
![]() |
5b5ff852f1 | ||
![]() |
7cbc799298 | ||
![]() |
14c0a6dab4 | ||
![]() |
742bf173eb | ||
![]() |
52e30c4b94 | ||
![]() |
a185f2325d | ||
![]() |
5eefa2baef | ||
![]() |
e56785e94d | ||
![]() |
0b7c86fa7e | ||
![]() |
a5ac2e9ca7 | ||
![]() |
ca4de6f317 | ||
![]() |
75dadc9610 | ||
![]() |
f6146b8732 | ||
![]() |
4025619b48 | ||
![]() |
9b1da76499 | ||
![]() |
4ca25eed70 | ||
![]() |
a925676d64 | ||
![]() |
6feb1a4525 | ||
![]() |
4cf7685aaf | ||
![]() |
e257a10525 | ||
![]() |
2e8548cb25 | ||
![]() |
1d25927676 | ||
![]() |
42d80f6866 | ||
![]() |
c9189ed7cb | ||
![]() |
fedefea0aa | ||
![]() |
437fa9a1a0 | ||
![]() |
0511ee277f | ||
![]() |
0b2bda529f | ||
![]() |
fe91744356 | ||
![]() |
45d92c1981 | ||
![]() |
58de834832 | ||
![]() |
8d89847d4b | ||
![]() |
bf78445ac6 | ||
![]() |
3866565e51 | ||
![]() |
564ab7d75e | ||
![]() |
24c0d31f14 | ||
![]() |
43a2bd98db | ||
![]() |
c4f56c40fd | ||
![]() |
1b4b66dbb1 | ||
![]() |
4787a5e22a | ||
![]() |
d2b85cbb10 | ||
![]() |
0428c2aeff | ||
![]() |
1a8789b50c | ||
![]() |
b5937e885d | ||
![]() |
8eef802ac7 | ||
![]() |
cad6afdc9d | ||
![]() |
54ef691a98 | ||
![]() |
426c2fd006 | ||
![]() |
f25c56b545 | ||
![]() |
960eb50d8c | ||
![]() |
18b4b76ebf | ||
![]() |
d9e575d4a2 | ||
![]() |
1769804c8a | ||
![]() |
81e094ac8f | ||
![]() |
4240c429ba | ||
![]() |
76f7d81129 | ||
![]() |
2da77f0f11 | ||
![]() |
580764b30c | ||
![]() |
afd69eef95 | ||
![]() |
be1cc67566 | ||
![]() |
b534708a99 | ||
![]() |
4bc75e0a9b | ||
![]() |
fa4c89e134 | ||
![]() |
5740372df9 | ||
![]() |
06791b9378 | ||
![]() |
91e927c913 | ||
![]() |
9a5c558060 | ||
![]() |
d1127a5238 | ||
![]() |
613a8b5792 | ||
![]() |
6f63371e9b | ||
![]() |
3b96e2eacd | ||
![]() |
ffdd6716f5 | ||
![]() |
4453862ea0 | ||
![]() |
c631d0738e | ||
![]() |
1bd7a7596b | ||
![]() |
23329cc522 | ||
![]() |
be86dc3c78 | ||
![]() |
d65a219194 | ||
![]() |
e18035b729 | ||
![]() |
96a1456dcd | ||
![]() |
83b92d84a6 | ||
![]() |
693bb50d5e | ||
![]() |
0cef0d3d9a | ||
![]() |
4ca187926c | ||
![]() |
7eda176162 | ||
![]() |
491a96825f | ||
![]() |
ffee3ccbeb | ||
![]() |
62ed920363 | ||
![]() |
4fc2c1e04c | ||
![]() |
91a33e65db | ||
![]() |
7b16deb3e3 | ||
![]() |
01d776957a | ||
![]() |
ad49800b09 | ||
![]() |
dce01350c7 | ||
![]() |
96bff03d99 | ||
![]() |
6eeb0eb47d | ||
![]() |
610ef73d38 | ||
![]() |
7d7732be76 | ||
![]() |
31a6d8174a | ||
![]() |
ee548b52db | ||
![]() |
1d77b0b059 | ||
![]() |
304562a2db | ||
![]() |
e358dc9cee | ||
![]() |
7573d83898 | ||
![]() |
00d9ed93ec | ||
![]() |
d3864ab02b | ||
![]() |
3b1e319a8e | ||
![]() |
33b34b84b5 | ||
![]() |
308996c4c1 | ||
![]() |
1f68b756d5 | ||
![]() |
ed6789cb8b | ||
![]() |
e9e86b07fb | ||
![]() |
a969b93ba6 | ||
![]() |
970cfadaec | ||
![]() |
12ed60d88e | ||
![]() |
e0e7817b8b | ||
![]() |
cb218697fa | ||
![]() |
3c167f35d4 | ||
![]() |
8a2736e0b7 | ||
![]() |
26edccd85f | ||
![]() |
91e6ed545e | ||
![]() |
c252aeec70 | ||
![]() |
2ce3b09650 | ||
![]() |
7548b160ae | ||
![]() |
1105f61988 | ||
![]() |
54b49d3322 | ||
![]() |
6ab0add430 | ||
![]() |
296288f428 | ||
![]() |
1d5fc01d65 | ||
![]() |
f30e376e11 | ||
![]() |
8ba02a77f1 | ||
![]() |
7cf3b959dd | ||
![]() |
ad584d553c | ||
![]() |
36556d559f | ||
![]() |
378611fe8f | ||
![]() |
35c890416d | ||
![]() |
ef16181ba2 | ||
![]() |
aaba0a09c2 | ||
![]() |
a7a1c626b6 | ||
![]() |
0b308769b1 | ||
![]() |
eb19d9fb11 | ||
![]() |
bfa271b089 | ||
![]() |
e299b27a5c | ||
![]() |
9306d93cbf | ||
![]() |
a28416dd9f | ||
![]() |
63b3af3f65 | ||
![]() |
fcc234c4fe | ||
![]() |
2e0ef7e0b0 | ||
![]() |
da99e1f1c3 | ||
![]() |
5827ad1581 | ||
![]() |
0e587211d9 | ||
![]() |
8b8ef80a5e | ||
![]() |
dc5ef487d2 | ||
![]() |
eaad93903b | ||
![]() |
a6b2bf1c55 | ||
![]() |
ae57fb9de0 | ||
![]() |
f4f0f7c3ed | ||
![]() |
7b183f0876 | ||
![]() |
e3a07b6c89 | ||
![]() |
0e8aecf84b | ||
![]() |
2f99d215d6 | ||
![]() |
9cb37de5ea | ||
![]() |
1b030a6fb3 | ||
![]() |
1f8cbe4b1d | ||
![]() |
438c6f23c8 | ||
![]() |
59c1efecb6 | ||
![]() |
d7e4164a46 | ||
![]() |
5023f60315 | ||
![]() |
bde4fcdd47 | ||
![]() |
69811c864e | ||
![]() |
c225452432 | ||
![]() |
5e60ea7f4c | ||
![]() |
5995a48d7b |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
4
.github/workflows/checkNewPlugins.yml
vendored
4
.github/workflows/checkNewPlugins.yml
vendored
@ -20,10 +20,10 @@ jobs:
|
|||||||
- name: Fetch origin repo
|
- name: Fetch origin repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/closeYoutrackOnCommit.yml
vendored
4
.github/workflows/closeYoutrackOnCommit.yml
vendored
@ -23,10 +23,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 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
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
|
||||||
|
14
.github/workflows/codeql-analysis.yml
vendored
14
.github/workflows/codeql-analysis.yml
vendored
@ -39,12 +39,18 @@ jobs:
|
|||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||||
|
java-version: '21'
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@ -55,7 +61,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@ -69,4 +75,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
4
.github/workflows/integrationsTest.yml
vendored
4
.github/workflows/integrationsTest.yml
vendored
@ -18,10 +18,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 300
|
fetch-depth: 300
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/kover.yml
vendored
4
.github/workflows/kover.yml
vendored
@ -18,10 +18,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 300
|
fetch-depth: 300
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/mergePr.yml
vendored
4
.github/workflows/mergePr.yml
vendored
@ -20,10 +20,10 @@ jobs:
|
|||||||
fetch-depth: 50
|
fetch-depth: 50
|
||||||
# See end of file updateChangeslog.yml for explanation of this secret
|
# See end of file updateChangeslog.yml for explanation of this secret
|
||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/runUiOctopusTests.yml
vendored
4
.github/workflows/runUiOctopusTests.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 21
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
# - name: Setup Gradle
|
# - name: Setup Gradle
|
||||||
@ -37,7 +37,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/IC-*/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
4
.github/workflows/runUiPyTests.yml
vendored
4
.github/workflows/runUiPyTests.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 21
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
@ -40,7 +40,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/PC-*/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
49
.github/workflows/runUiRdTests.yml
vendored
Normal file
49
.github/workflows/runUiRdTests.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: Run UI Rider Tests
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 12 * * *'
|
||||||
|
jobs:
|
||||||
|
build-for-ui-test-mac-os:
|
||||||
|
if: github.repository == 'JetBrains/ideavim'
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: zulu
|
||||||
|
java-version: 21
|
||||||
|
- name: Setup FFmpeg
|
||||||
|
run: brew install ffmpeg
|
||||||
|
# - name: Setup Gradle
|
||||||
|
# uses: gradle/gradle-build-action@v2.4.2
|
||||||
|
- name: Build Plugin
|
||||||
|
run: gradle :buildPlugin
|
||||||
|
- name: Run Idea
|
||||||
|
run: |
|
||||||
|
mkdir -p build/reports
|
||||||
|
gradle --no-configuration-cache :runIdeForUiTests -PideaType=RD > build/reports/idea.log &
|
||||||
|
- name: Wait for Idea started
|
||||||
|
uses: jtalk/url-health-check-action@v3
|
||||||
|
with:
|
||||||
|
url: http://127.0.0.1:8082
|
||||||
|
max-attempts: 20
|
||||||
|
retry-delay: 10s
|
||||||
|
- name: Tests
|
||||||
|
run: gradle :tests:ui-rd-tests:testUi
|
||||||
|
- name: Move video
|
||||||
|
if: always()
|
||||||
|
run: mv tests/ui-rd-tests/video build/reports
|
||||||
|
- name: Move sandbox logs
|
||||||
|
if: always()
|
||||||
|
run: mv build/idea-sandbox/RD-*/log_runIdeForUiTests idea-sandbox-log
|
||||||
|
- name: Save report
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ui-test-fails-report-mac
|
||||||
|
path: |
|
||||||
|
build/reports
|
||||||
|
tests/ui-rd-tests/build/reports
|
||||||
|
idea-sandbox-log
|
4
.github/workflows/runUiTests.yml
vendored
4
.github/workflows/runUiTests.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: zulu
|
distribution: zulu
|
||||||
java-version: 17
|
java-version: 21
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
# - name: Setup Gradle
|
# - name: Setup Gradle
|
||||||
@ -37,7 +37,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/IC-*/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
4
.github/workflows/updateAuthors.yml
vendored
4
.github/workflows/updateAuthors.yml
vendored
@ -25,10 +25,10 @@ jobs:
|
|||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --tags origin
|
run: git fetch --tags origin
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/updateChangelog.yml
vendored
4
.github/workflows/updateChangelog.yml
vendored
@ -22,10 +22,10 @@ jobs:
|
|||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Get tags
|
- name: Get tags
|
||||||
run: git fetch --tags origin
|
run: git fetch --tags origin
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
4
.github/workflows/updateFormatting.yml
vendored
4
.github/workflows/updateFormatting.yml
vendored
@ -20,10 +20,10 @@ jobs:
|
|||||||
fetch-depth: 50
|
fetch-depth: 50
|
||||||
# See end of file updateChangeslog.yml for explanation of this secret
|
# See end of file updateChangeslog.yml for explanation of this secret
|
||||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 21
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '21'
|
||||||
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
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@
|
|||||||
!/.idea/vcs.xml
|
!/.idea/vcs.xml
|
||||||
!/.idea/misc.xml
|
!/.idea/misc.xml
|
||||||
!/.idea/.name
|
!/.idea/.name
|
||||||
|
!/.idea/gradle.xml
|
||||||
|
|
||||||
**/build/
|
**/build/
|
||||||
**/out/
|
**/out/
|
||||||
|
27
.idea/gradle.xml
Normal file
27
.idea/gradle.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/annotation-processors" />
|
||||||
|
<option value="$PROJECT_DIR$/scripts" />
|
||||||
|
<option value="$PROJECT_DIR$/tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/java-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/long-running-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/property-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/ui-fixtures" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/ui-ij-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/ui-py-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/tests/ui-rd-tests" />
|
||||||
|
<option value="$PROJECT_DIR$/vim-engine" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -18,5 +18,5 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
13
.teamcity/_Self/Constants.kt
vendored
13
.teamcity/_Self/Constants.kt
vendored
@ -5,12 +5,11 @@ 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 NVIM_TESTS = "2024.3.3"
|
||||||
const val PROPERTY_TESTS = "2024.2.1"
|
const val PROPERTY_TESTS = "2024.3.3"
|
||||||
const val LONG_RUNNING_TESTS = "2024.2.1"
|
const val LONG_RUNNING_TESTS = "2024.3.3"
|
||||||
const val QODANA_TESTS = "2024.2.1"
|
const val RELEASE = "2024.3.3"
|
||||||
const val RELEASE = "2024.2.1"
|
|
||||||
|
|
||||||
const val RELEASE_DEV = "2024.2.1"
|
const val RELEASE_DEV = "2024.3.3"
|
||||||
const val RELEASE_EAP = "2024.2.1"
|
const val RELEASE_EAP = "2024.3.3"
|
||||||
}
|
}
|
||||||
|
5
.teamcity/_Self/Project.kt
vendored
5
.teamcity/_Self/Project.kt
vendored
@ -5,7 +5,6 @@ import _Self.buildTypes.LongRunning
|
|||||||
import _Self.buildTypes.Nvim
|
import _Self.buildTypes.Nvim
|
||||||
import _Self.buildTypes.PluginVerifier
|
import _Self.buildTypes.PluginVerifier
|
||||||
import _Self.buildTypes.PropertyBased
|
import _Self.buildTypes.PropertyBased
|
||||||
import _Self.buildTypes.Qodana
|
|
||||||
import _Self.buildTypes.TestingBuildType
|
import _Self.buildTypes.TestingBuildType
|
||||||
import _Self.subprojects.GitHub
|
import _Self.subprojects.GitHub
|
||||||
import _Self.subprojects.Releases
|
import _Self.subprojects.Releases
|
||||||
@ -25,7 +24,7 @@ object Project : Project({
|
|||||||
|
|
||||||
// Active tests
|
// Active tests
|
||||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
buildType(TestingBuildType("2024.2.1", "<default>"))
|
buildType(TestingBuildType("2024.3.3", "<default>"))
|
||||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
|
|
||||||
buildType(PropertyBased)
|
buildType(PropertyBased)
|
||||||
@ -34,8 +33,6 @@ object Project : Project({
|
|||||||
buildType(Nvim)
|
buildType(Nvim)
|
||||||
buildType(PluginVerifier)
|
buildType(PluginVerifier)
|
||||||
buildType(Compatibility)
|
buildType(Compatibility)
|
||||||
|
|
||||||
buildType(Qodana)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Common build type for all configurations
|
// Common build type for all configurations
|
||||||
|
20
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
20
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
@ -20,7 +20,7 @@ object Compatibility : IdeaVimBuildType({
|
|||||||
name = "Load Verifier"
|
name = "Load Verifier"
|
||||||
scriptContent = """
|
scriptContent = """
|
||||||
mkdir verifier1
|
mkdir verifier1
|
||||||
curl -f -L -o verifier1/verifier-cli-dev-all-1.jar "https://packages.jetbrains.team/files/p/ideavim/plugin-verifier/verifier-cli-dev-all-1.jar"
|
curl -f -L -o verifier1/verifier-cli-dev-all-2.jar "https://packages.jetbrains.team/files/p/ideavim/plugin-verifier/verifier-cli-dev-all-2.jar"
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
script {
|
script {
|
||||||
@ -33,16 +33,16 @@ object Compatibility : IdeaVimBuildType({
|
|||||||
# Upload verifier-cli-dev-all.jar artifact to the repo in IdeaVim space repo
|
# Upload verifier-cli-dev-all.jar artifact to the repo in IdeaVim space repo
|
||||||
|
|
||||||
java --version
|
java --version
|
||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [latest-IU] -team-city
|
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [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-2.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-2.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-2.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-2.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-2.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-2.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-2.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
|
java -jar verifier1/verifier-cli-dev-all-2.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
1
.teamcity/_Self/buildTypes/LongRunning.kt
vendored
1
.teamcity/_Self/buildTypes/LongRunning.kt
vendored
@ -28,6 +28,7 @@ object LongRunning : IdeaVimBuildType({
|
|||||||
tasks = "clean :tests:long-running-tests:testLongRunning"
|
tasks = "clean :tests:long-running-tests:testLongRunning"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
.teamcity/_Self/buildTypes/Nvim.kt
vendored
9
.teamcity/_Self/buildTypes/Nvim.kt
vendored
@ -18,7 +18,7 @@ object Nvim : IdeaVimBuildType({
|
|||||||
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
|
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", NVIM_TESTS)
|
param("env.ORG_GRADLE_PROJECT_ideaVersion", NVIM_TESTS)
|
||||||
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
|
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
|
||||||
param("env.ideavim.nvim.path", "./nvim-linux64/bin/nvim")
|
param("env.ideavim.nvim.path", "./nvim-linux-x86_64/bin/nvim")
|
||||||
}
|
}
|
||||||
|
|
||||||
vcs {
|
vcs {
|
||||||
@ -32,9 +32,9 @@ object Nvim : IdeaVimBuildType({
|
|||||||
script {
|
script {
|
||||||
name = "Set up NeoVim"
|
name = "Set up NeoVim"
|
||||||
scriptContent = """
|
scriptContent = """
|
||||||
wget https://github.com/neovim/neovim/releases/download/v0.7.2/nvim-linux64.tar.gz
|
wget https://github.com/neovim/neovim/releases/download/nightly/nvim-linux-x86_64.tar.gz
|
||||||
tar xzf nvim-linux64.tar.gz
|
tar xzf nvim-linux-x86_64.tar.gz
|
||||||
cd nvim-linux64/bin
|
cd nvim-linux-x86_64/bin
|
||||||
chmod +x nvim
|
chmod +x nvim
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
@ -42,6 +42,7 @@ object Nvim : IdeaVimBuildType({
|
|||||||
tasks = "clean test -Dnvim"
|
tasks = "clean test -Dnvim"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
1
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
@ -25,6 +25,7 @@ object PluginVerifier : IdeaVimBuildType({
|
|||||||
tasks = "clean verifyPlugin"
|
tasks = "clean verifyPlugin"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003-2024 The IdeaVim authors
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style
|
|
||||||
* license that can be found in the LICENSE.txt file or at
|
|
||||||
* https://opensource.org/licenses/MIT.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.IdeaVimBuildType
|
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
|
||||||
|
|
||||||
object PrintReleaseBranch : IdeaVimBuildType({
|
|
||||||
name = "EXP: Print release branch"
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(ReleasesVcsRoot)
|
|
||||||
branchFilter = "+:heads/releases/*"
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
|
|
||||||
script {
|
|
||||||
name = "Print current branch"
|
|
||||||
scriptContent = """
|
|
||||||
echo "Current branch is: %teamcity.build.branch%"
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
sshAgent {
|
|
||||||
teamcitySshKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
1
.teamcity/_Self/buildTypes/PropertyBased.kt
vendored
1
.teamcity/_Self/buildTypes/PropertyBased.kt
vendored
@ -27,6 +27,7 @@ object PropertyBased : IdeaVimBuildType({
|
|||||||
tasks = "clean :tests:property-tests:testPropertyBased"
|
tasks = "clean :tests:property-tests:testPropertyBased"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ object PublishVimEngine : IdeaVimBuildType({
|
|||||||
tasks = ":vim-engine:publish"
|
tasks = ":vim-engine:publish"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
80
.teamcity/_Self/buildTypes/Qodana.kt
vendored
80
.teamcity/_Self/buildTypes/Qodana.kt
vendored
@ -1,80 +0,0 @@
|
|||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.Constants.QODANA_TESTS
|
|
||||||
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.buildSteps.Qodana
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.qodana
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
|
|
||||||
|
|
||||||
object Qodana : IdeaVimBuildType({
|
|
||||||
name = "Qodana checks"
|
|
||||||
params {
|
|
||||||
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
|
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", QODANA_TESTS)
|
|
||||||
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
|
|
||||||
}
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(DslContext.settingsRoot)
|
|
||||||
branchFilter = "+:<default>"
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
gradle {
|
|
||||||
name = "Generate grammar"
|
|
||||||
tasks = "generateGrammarSource"
|
|
||||||
}
|
|
||||||
qodana {
|
|
||||||
name = "Qodana"
|
|
||||||
param("clonefinder-languages", "")
|
|
||||||
param("collect-anonymous-statistics", "")
|
|
||||||
param("licenseaudit-enable", "")
|
|
||||||
param("clonefinder-languages-container", "")
|
|
||||||
param("linterVersion", "")
|
|
||||||
param("clonefinder-queried-project", "")
|
|
||||||
param("clonefinder-enable", "")
|
|
||||||
param("clonefinder-reference-projects", "")
|
|
||||||
linter = jvm {
|
|
||||||
version = Qodana.JVMVersion.LATEST
|
|
||||||
}
|
|
||||||
reportAsTests = true
|
|
||||||
additionalQodanaArguments = "--baseline qodana.sarif.json"
|
|
||||||
cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
triggers {
|
|
||||||
vcs {
|
|
||||||
enabled = false
|
|
||||||
branchFilter = "+:<default>"
|
|
||||||
}
|
|
||||||
schedule {
|
|
||||||
schedulingPolicy = daily {
|
|
||||||
hour = 12
|
|
||||||
minute = 0
|
|
||||||
timezone = "SERVER"
|
|
||||||
}
|
|
||||||
param("dayOfWeek", "Sunday")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
failureConditions {
|
|
||||||
failOnMetricChange {
|
|
||||||
threshold = 0
|
|
||||||
units = BuildFailureOnMetric.MetricUnit.DEFAULT_UNIT
|
|
||||||
comparison = BuildFailureOnMetric.MetricComparison.MORE
|
|
||||||
compareTo = value()
|
|
||||||
metric = BuildFailureOnMetric.MetricType.TEST_FAILED_COUNT
|
|
||||||
param("metricKey", "QodanaProblemsNew")
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
3
.teamcity/_Self/buildTypes/ReleaseDev.kt
vendored
3
.teamcity/_Self/buildTypes/ReleaseDev.kt
vendored
@ -47,13 +47,16 @@ object ReleaseDev : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Calculate new dev version"
|
name = "Calculate new dev version"
|
||||||
tasks = "scripts:calculateNewDevVersion"
|
tasks = "scripts:calculateNewDevVersion"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
name = "Set TeamCity build number"
|
name = "Set TeamCity build number"
|
||||||
tasks = "scripts:setTeamCityBuildNumber"
|
tasks = "scripts:setTeamCityBuildNumber"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
tasks = "publishPlugin"
|
tasks = "publishPlugin"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
.teamcity/_Self/buildTypes/ReleaseEap.kt
vendored
5
.teamcity/_Self/buildTypes/ReleaseEap.kt
vendored
@ -57,18 +57,22 @@ object ReleaseEap : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Calculate new eap version"
|
name = "Calculate new eap version"
|
||||||
tasks = "scripts:calculateNewEapVersion"
|
tasks = "scripts:calculateNewEapVersion"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
name = "Set TeamCity build number"
|
name = "Set TeamCity build number"
|
||||||
tasks = "scripts:setTeamCityBuildNumber"
|
tasks = "scripts:setTeamCityBuildNumber"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
name = "Add release tag"
|
name = "Add release tag"
|
||||||
tasks = "scripts:addReleaseTag"
|
tasks = "scripts:addReleaseTag"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
name = "Publish plugin"
|
name = "Publish plugin"
|
||||||
tasks = "publishPlugin"
|
tasks = "publishPlugin"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
script {
|
script {
|
||||||
name = "Push changes to the repo"
|
name = "Push changes to the repo"
|
||||||
@ -86,6 +90,7 @@ object ReleaseEap : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "YouTrack post release actions"
|
name = "YouTrack post release actions"
|
||||||
tasks = "scripts:eapReleaseActions"
|
tasks = "scripts:eapReleaseActions"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
@ -1,106 +0,0 @@
|
|||||||
package _Self.buildTypes
|
|
||||||
|
|
||||||
import _Self.Constants.EAP_CHANNEL
|
|
||||||
import _Self.Constants.RELEASE_EAP
|
|
||||||
import _Self.IdeaVimBuildType
|
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
|
||||||
|
|
||||||
object ReleaseEapFromBranch : IdeaVimBuildType({
|
|
||||||
name = "EXP: Publish EAP Build from branch"
|
|
||||||
description = "Build and publish EAP of IdeaVim plugin"
|
|
||||||
|
|
||||||
artifactRules = "build/distributions/*"
|
|
||||||
|
|
||||||
params {
|
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE_EAP)
|
|
||||||
password(
|
|
||||||
"env.ORG_GRADLE_PROJECT_publishToken",
|
|
||||||
"credentialsJSON:61a36031-4da1-4226-a876-b8148bf32bde",
|
|
||||||
label = "Password"
|
|
||||||
)
|
|
||||||
param("env.ORG_GRADLE_PROJECT_publishChannels", EAP_CHANNEL)
|
|
||||||
password(
|
|
||||||
"env.ORG_GRADLE_PROJECT_slackUrl",
|
|
||||||
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
|
|
||||||
label = "Slack Token"
|
|
||||||
)
|
|
||||||
password(
|
|
||||||
"env.YOUTRACK_TOKEN",
|
|
||||||
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
|
|
||||||
display = ParameterDisplay.HIDDEN
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
vcs {
|
|
||||||
root(ReleasesVcsRoot)
|
|
||||||
branchFilter = """
|
|
||||||
+:heads/releases/*
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
|
||||||
}
|
|
||||||
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
name = "Pull git tags"
|
|
||||||
scriptContent = "git fetch --tags origin"
|
|
||||||
}
|
|
||||||
script {
|
|
||||||
name = "Pull git history"
|
|
||||||
scriptContent = "git fetch --unshallow"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Calculate new eap version from branch"
|
|
||||||
tasks = "scripts:calculateNewEapVersionFromBranch"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Set TeamCity build number"
|
|
||||||
tasks = "scripts:setTeamCityBuildNumber"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Add release tag"
|
|
||||||
tasks = "scripts:addReleaseTag"
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "Publish plugin"
|
|
||||||
tasks = "publishPlugin"
|
|
||||||
}
|
|
||||||
script {
|
|
||||||
name = "Push changes to the repo"
|
|
||||||
scriptContent = """
|
|
||||||
branch=$(git branch --show-current)
|
|
||||||
echo current branch is ${'$'}branch
|
|
||||||
git push origin %build.number%
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
gradle {
|
|
||||||
name = "YouTrack post release actions"
|
|
||||||
tasks = "scripts:eapReleaseActions"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
features {
|
|
||||||
sshAgent {
|
|
||||||
teamcitySshKey = "IdeaVim ssh keys"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
failureConditions {
|
|
||||||
failOnMetricChange {
|
|
||||||
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
|
|
||||||
threshold = 5
|
|
||||||
units = BuildFailureOnMetric.MetricUnit.PERCENTS
|
|
||||||
comparison = BuildFailureOnMetric.MetricComparison.DIFF
|
|
||||||
compareTo = build {
|
|
||||||
buildRule = lastSuccessful()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
10
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
10
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@ -28,7 +28,10 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
name = "Publish $releaseType release"
|
name = "Publish $releaseType release"
|
||||||
description = "Build and publish IdeaVim plugin"
|
description = "Build and publish IdeaVim plugin"
|
||||||
|
|
||||||
artifactRules = "build/distributions/*"
|
artifactRules = """
|
||||||
|
build/distributions/*
|
||||||
|
build/reports/*
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
params {
|
params {
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE)
|
param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE)
|
||||||
@ -90,10 +93,12 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Calculate new version"
|
name = "Calculate new version"
|
||||||
tasks = "scripts:calculateNewVersion"
|
tasks = "scripts:calculateNewVersion"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
gradle {
|
gradle {
|
||||||
name = "Set TeamCity build number"
|
name = "Set TeamCity build number"
|
||||||
tasks = "scripts:setTeamCityBuildNumber"
|
tasks = "scripts:setTeamCityBuildNumber"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
// gradle {
|
// gradle {
|
||||||
// name = "Update change log"
|
// name = "Update change log"
|
||||||
@ -106,6 +111,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Add release tag"
|
name = "Add release tag"
|
||||||
tasks = "scripts:addReleaseTag"
|
tasks = "scripts:addReleaseTag"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
script {
|
script {
|
||||||
name = "Run tests"
|
name = "Run tests"
|
||||||
@ -114,6 +120,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Publish release"
|
name = "Publish release"
|
||||||
tasks = "publishPlugin"
|
tasks = "publishPlugin"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
// script {
|
// script {
|
||||||
// name = "Checkout master branch"
|
// name = "Checkout master branch"
|
||||||
@ -145,6 +152,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
name = "Run Integrations"
|
name = "Run Integrations"
|
||||||
tasks = "releaseActions"
|
tasks = "releaseActions"
|
||||||
gradleParams = "--no-configuration-cache"
|
gradleParams = "--no-configuration-cache"
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
// gradle {
|
// gradle {
|
||||||
// name = "Slack Notification"
|
// name = "Slack Notification"
|
||||||
|
@ -43,7 +43,7 @@ open class TestingBuildType(
|
|||||||
tasks = "clean test"
|
tasks = "clean test"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
.teamcity/_Self/subprojects/GitHub.kt
vendored
1
.teamcity/_Self/subprojects/GitHub.kt
vendored
@ -42,6 +42,7 @@ class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({
|
|||||||
tasks = command
|
tasks = command
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-21-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
.teamcity/_Self/subprojects/Releases.kt
vendored
7
.teamcity/_Self/subprojects/Releases.kt
vendored
@ -1,11 +1,8 @@
|
|||||||
package _Self.subprojects
|
package _Self.subprojects
|
||||||
|
|
||||||
import _Self.buildTypes.CreateNewReleaseBranchFromMaster
|
|
||||||
import _Self.buildTypes.PrintReleaseBranch
|
|
||||||
import _Self.buildTypes.PublishVimEngine
|
import _Self.buildTypes.PublishVimEngine
|
||||||
import _Self.buildTypes.ReleaseDev
|
import _Self.buildTypes.ReleaseDev
|
||||||
import _Self.buildTypes.ReleaseEap
|
import _Self.buildTypes.ReleaseEap
|
||||||
import _Self.buildTypes.ReleaseEapFromBranch
|
|
||||||
import _Self.buildTypes.ReleaseMajor
|
import _Self.buildTypes.ReleaseMajor
|
||||||
import _Self.buildTypes.ReleaseMinor
|
import _Self.buildTypes.ReleaseMinor
|
||||||
import _Self.buildTypes.ReleasePatch
|
import _Self.buildTypes.ReleasePatch
|
||||||
@ -41,8 +38,4 @@ object Releases : Project({
|
|||||||
buildType(ReleaseEap)
|
buildType(ReleaseEap)
|
||||||
buildType(ReleaseDev)
|
buildType(ReleaseDev)
|
||||||
buildType(PublishVimEngine)
|
buildType(PublishVimEngine)
|
||||||
|
|
||||||
buildType(CreateNewReleaseBranchFromMaster)
|
|
||||||
buildType(PrintReleaseBranch)
|
|
||||||
buildType(ReleaseEapFromBranch)
|
|
||||||
})
|
})
|
||||||
|
2
.teamcity/settings.kts
vendored
2
.teamcity/settings.kts
vendored
@ -30,5 +30,5 @@ node (Plugins -> teamcity-configs -> teamcity-configs:generate),
|
|||||||
the 'Debug' option is available in the context menu for the task.
|
the 'Debug' option is available in the context menu for the task.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
version = "2024.03"
|
version = "2024.12"
|
||||||
project(_Self.Project)
|
project(_Self.Project)
|
||||||
|
36
AUTHORS.md
36
AUTHORS.md
@ -498,7 +498,7 @@ Contributors:
|
|||||||
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
|
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
|
||||||
[![icon][github]](https://github.com/lippfi)
|
[![icon][github]](https://github.com/lippfi)
|
||||||
|
|
||||||
lippfi,
|
lippfi
|
||||||
* [![icon][mail]](mailto:fillipser143@gmail.com)
|
* [![icon][mail]](mailto:fillipser143@gmail.com)
|
||||||
[![icon][github]](https://github.com/Parker7123)
|
[![icon][github]](https://github.com/Parker7123)
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ Contributors:
|
|||||||
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
|
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
|
||||||
[![icon][github]](https://github.com/kun-codes)
|
[![icon][github]](https://github.com/kun-codes)
|
||||||
|
|
||||||
Bishwa Saha,
|
Bishwa Saha
|
||||||
* [![icon][mail]](mailto:alexfu@fastmail.com)
|
* [![icon][mail]](mailto:alexfu@fastmail.com)
|
||||||
[![icon][github]](https://github.com/alexfu)
|
[![icon][github]](https://github.com/alexfu)
|
||||||
|
|
||||||
@ -542,7 +542,7 @@ Contributors:
|
|||||||
* [![icon][mail]](mailto:kirill.karnaukhov@jetbrains.com)
|
* [![icon][mail]](mailto:kirill.karnaukhov@jetbrains.com)
|
||||||
[![icon][github]](https://github.com/kkarnauk)
|
[![icon][github]](https://github.com/kkarnauk)
|
||||||
|
|
||||||
Kirill Karnaukhov,
|
Kirill Karnaukhov
|
||||||
* [![icon][mail]](mailto:sander.hestvik@gmail.com)
|
* [![icon][mail]](mailto:sander.hestvik@gmail.com)
|
||||||
[![icon][github]](https://github.com/SanderHestvik)
|
[![icon][github]](https://github.com/SanderHestvik)
|
||||||
|
|
||||||
@ -558,11 +558,35 @@ Contributors:
|
|||||||
* [![icon][mail]](mailto:j.trimailovas@gmail.com)
|
* [![icon][mail]](mailto:j.trimailovas@gmail.com)
|
||||||
[![icon][github]](https://github.com/trimailov)
|
[![icon][github]](https://github.com/trimailov)
|
||||||
|
|
||||||
Justas Trimailovas,
|
Justas Trimailovas
|
||||||
* [![icon][mail]](mailto:justast@wix.com)
|
* [![icon][mail]](mailto:justast@wix.com)
|
||||||
[![icon][github]](https://github.com/justast-wix)
|
[![icon][github]](https://github.com/justast-wix)
|
||||||
|
|
||||||
Justas Trimailovas
|
Justas Trimailovas
|
||||||
|
* [![icon][mail]](mailto:wangxinhe06@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/wxh06)
|
||||||
|
|
||||||
|
Xinhe Wang
|
||||||
|
* [![icon][mail]](mailto:vladimir.parfinenko@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/cypok)
|
||||||
|
|
||||||
|
Vladimir Parfinenko
|
||||||
|
* [![icon][mail]](mailto:sdoerner@google.com)
|
||||||
|
[![icon][github]](https://github.com/sdoerner)
|
||||||
|
|
||||||
|
Sebastian Dörner
|
||||||
|
* [![icon][mail]](mailto:ocordova@pulsarml.com)
|
||||||
|
[![icon][github]](https://github.com/oca159)
|
||||||
|
|
||||||
|
Osvaldo
|
||||||
|
* [![icon][mail]](mailto:nath@squareup.com)
|
||||||
|
[![icon][github]](https://github.com/nath)
|
||||||
|
|
||||||
|
Nath Tumlin
|
||||||
|
* [![icon][mail]](mailto:ilya.usov@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/Iliya-usov)
|
||||||
|
|
||||||
|
Ilya Usov
|
||||||
|
|
||||||
Previous contributors:
|
Previous contributors:
|
||||||
|
|
||||||
@ -574,10 +598,6 @@ Previous contributors:
|
|||||||
[![icon][github]](https://github.com/kevin70)
|
[![icon][github]](https://github.com/kevin70)
|
||||||
|
|
||||||
kk
|
kk
|
||||||
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
|
|
||||||
[![icon][github]](https://github.com/gregsh)
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -1,26 +1,50 @@
|
|||||||
[![TeamCity Build][teamcity-build-status-svg]][teamcity-build-status]
|
[![TeamCity Build][teamcity-build-status-svg]][teamcity-build-status]
|
||||||
|
|
||||||
IdeaVim is an open source project created by 80+ contributors. Would you like to make it even better? That’s wonderful!
|
IdeaVim is an open source project created by 130+ contributors. Would you like to make it even better? That’s wonderful!
|
||||||
|
|
||||||
This page is created to help you start contributing. And who knows, maybe in a few days this project will be brighter than ever!
|
This page is created to help you start contributing. And who knows, maybe in a few days this project will be brighter than ever!
|
||||||
|
|
||||||
:warning: The plugin is currently under a huge refactoring aiming to split into vim-engine and IdeaVim in order to
|
# Awards for Quality Contributions
|
||||||
support the new [Fleet IDE](https://www.jetbrains.com/fleet/). Please see [Fleet refactoring](#Fleet-refactoring).
|
|
||||||
|
In February 2025, we’re starting a program to award one-year All Products Pack subscriptions to the implementers of quality contributions to the IdeaVim project. The program will continue for all of 2025 and may be prolonged.
|
||||||
|
|
||||||
|
Subscriptions can be awarded for merged pull requests that meet the following requirements:
|
||||||
|
|
||||||
|
|
||||||
|
- The change should be non-trivial, though there might be exceptions — for example, where a trivial fix requires a complicated investigation.
|
||||||
|
- The change should fully implement a feature or fix the root cause of a bug. Workarounds or hacks are not accepted.
|
||||||
|
- If applicable, the change should be properly covered with unit tests.
|
||||||
|
- The work should be performed by the contributor, though the IdeaVim team is happy to review it and give feedback.
|
||||||
|
- The change should fix an issue or implement a feature filed by another user. If you want to file an issue and provide a solution to it, your request for a license should be explicitly discussed with the IdeaVim team in the ticket comments.
|
||||||
|
|
||||||
|
|
||||||
|
We'd like to make sure this award program is helpful and fair. Since we just started it and still fine-tuning the details, the final say on giving licenses remains with the IdeaVim team and the requirements might evolve over time.
|
||||||
|
|
||||||
|
|
||||||
|
Also, a few notes:
|
||||||
|
|
||||||
|
|
||||||
|
- If you have any doubts about whether your change or fix is eligible for the award, get in touch with us in the comments on YouTrack or in any other way.
|
||||||
|
- Please mention this program in the pull request text. This is not an absolute requirement, but it will help ensure we know you would like to be considered for an award, but this is not required.
|
||||||
|
- During 2025, a single person may only receive a single subscription. Even if you make multiple contributions, you will not be eligible for multiple awards.
|
||||||
|
- Any delays caused by the IdeaVim team will not affect eligibility for an award if the other requirements are met.
|
||||||
|
- Draft pull requests will not be reviewed unless explicitly requested.
|
||||||
|
- Tickets with the [ideavim-bounty](https://youtrack.jetbrains.com/issues?q=tag:%20%7BIdeaVim-bounty%7D) tag are good candidates for this award.
|
||||||
|
|
||||||
|
|
||||||
## Before you begin
|
## Before you begin
|
||||||
|
|
||||||
- The project is written in Kotlin and Java. Choose whichever language you feel more comfortable with,
|
- The project is primarily written in Kotlin with a few Java files. When contributing to the project, use Kotlin unless
|
||||||
or maybe one that you’d like to get to know better (why not start [learning Kotlin](https://kotlinlang.org/docs/tutorials/) right now?).
|
you’re working in areas where Java is explicitly used.
|
||||||
|
|
||||||
- If you come across some IntelliJ Platform code, these links may prove helpful:
|
- If you come across some IntelliJ Platform code, these links may prove helpful:
|
||||||
|
|
||||||
* [IntelliJ architectural overview](https://www.jetbrains.org/intellij/sdk/docs/platform/fundamentals.html)
|
* [IntelliJ Platform SDK](https://plugins.jetbrains.com/docs/intellij/welcome.html)
|
||||||
* [IntelliJ plugin development resources](https://www.jetbrains.org/intellij/sdk/docs/welcome.html)
|
* [IntelliJ architectural overview](https://plugins.jetbrains.com/docs/intellij/fundamentals.html)
|
||||||
|
* [IntelliJ Platform community space](https://platform.jetbrains.com/)
|
||||||
|
|
||||||
- Having any difficulties?
|
- Having any difficulties?
|
||||||
Join the brand new
|
Ask any questions in [GitHub discussions](https://github.com/JetBrains/ideavim/discussions) or [IntelliJ Platform community space](https://platform.jetbrains.com/).
|
||||||
[](https://gitter.im/JetBrains/ideavim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
for IdeaVim developers and contributors!
|
|
||||||
|
|
||||||
OK, ready to do some coding?
|
OK, ready to do some coding?
|
||||||
|
|
||||||
@ -82,7 +106,7 @@ If you are looking for:
|
|||||||
|
|
||||||
- Common features:
|
- Common features:
|
||||||
- State machine. How every particular keystroke is parsed in IdeaVim: `KeyHandler.handleKey()`.
|
- State machine. How every particular keystroke is parsed in IdeaVim: `KeyHandler.handleKey()`.
|
||||||
- Options (`incsearch`, `iskeyword`, `relativenumber`): `OptionServiceImpl`.
|
- Options (`incsearch`, `iskeyword`, `relativenumber`): `VimOptionGroup`.
|
||||||
- Plugin startup: `PluginStartup`.
|
- Plugin startup: `PluginStartup`.
|
||||||
- Notifications: `NotificationService`.
|
- Notifications: `NotificationService`.
|
||||||
- Status bar icon: `StatusBar.kt`.
|
- Status bar icon: `StatusBar.kt`.
|
||||||
@ -110,7 +134,7 @@ Cras id tellus in ex imperdiet egestas.
|
|||||||
carets, dollar motion, etc.
|
carets, dollar motion, etc.
|
||||||
|
|
||||||
##### Neovim
|
##### Neovim
|
||||||
IdeaVim has an experimental integration with neovim in tests. Tests that are performed with `doTest` also executed in
|
IdeaVim has an integration with neovim in tests. Tests that are performed with `doTest` also executed in
|
||||||
neovim instance, and the state of IdeaVim is asserted to be the same as the state of neovim.
|
neovim instance, and the state of IdeaVim is asserted to be the same as the state of neovim.
|
||||||
- Only tests that use `doTest` are checked with neovim.
|
- Only tests that use `doTest` are checked with neovim.
|
||||||
- Tests with `@VimBehaviorDiffers` or `@TestWithoutNeovim` annotations don't use neovim.
|
- Tests with `@VimBehaviorDiffers` or `@TestWithoutNeovim` annotations don't use neovim.
|
||||||
@ -136,14 +160,15 @@ We also support proper command mappings (functions are mapped to `<Plug>...`), t
|
|||||||
- Magic is supported as well. See `Magic`.
|
- Magic is supported as well. See `Magic`.
|
||||||
|
|
||||||
|
|
||||||
## Fleet refactoring
|
## Fleet
|
||||||
At the moment, IdeaVim is under an active refactoring aiming to split IdeaVim into two modules: vim-engine and IdeaVim.
|
|
||||||
|
The IdeaVim plugin is divided into two main modules: IdeaVim and vim-engine.
|
||||||
|
IdeaVim serves as a plugin for JetBrains IDEs, while vim-engine is an IntelliJ Platform-independent Vim engine.
|
||||||
|
This engine is utilized in both the Vim plugin for Fleet and IdeaVim.
|
||||||
|
|
||||||
If you develop a plugin that depends on IdeaVim: We have an instrument to check that our changes don't affect
|
If you develop a plugin that depends on IdeaVim: We have an instrument to check that our changes don't affect
|
||||||
the plugins in the marketplace. Also, we commit to support currently used API at least till the end of 2022.
|
the plugins in the marketplace.
|
||||||
If you still encounter any issues with the newer versions of IdeaVim, please [contact maintainers](https://github.com/JetBrains/ideavim#contact-maintainers).
|
If you still encounter any issues with the newer versions of IdeaVim, please [contact maintainers](https://github.com/JetBrains/ideavim#contact-maintainers).
|
||||||
We kindly ask you not to use anything from the new API (like `VimEditor`, `injector`) because at the moment we don't
|
|
||||||
guarantee the compatibility of this API in the future versions.
|
|
||||||
|
|
||||||
|
|
||||||
-----
|
-----
|
||||||
@ -166,6 +191,7 @@ This is just terrible. [You know what to do](https://github.com/JetBrains/ideavi
|
|||||||
|
|
||||||
* [Continuous integration builds](https://ideavim.teamcity.com/)
|
* [Continuous integration builds](https://ideavim.teamcity.com/)
|
||||||
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
|
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
|
||||||
|
* [IntelliJ Platform community space](https://platform.jetbrains.com/)
|
||||||
* [Chat on gitter](https://gitter.im/JetBrains/ideavim)
|
* [Chat on gitter](https://gitter.im/JetBrains/ideavim)
|
||||||
* [IdeaVim Channel](https://jb.gg/bi6zp7) on [JetBrains Server](https://discord.gg/jetbrains)
|
* [IdeaVim Channel](https://jb.gg/bi6zp7) on [JetBrains Server](https://discord.gg/jetbrains)
|
||||||
* [Plugin homepage](https://plugins.jetbrains.com/plugin/164-ideavim)
|
* [Plugin homepage](https://plugins.jetbrains.com/plugin/164-ideavim)
|
||||||
|
20
README.md
20
README.md
@ -29,7 +29,7 @@ IdeaVim is a Vim engine for JetBrains IDEs.
|
|||||||
|
|
||||||
#### Compatibility
|
#### Compatibility
|
||||||
|
|
||||||
IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm, RubyMine, AppCode, DataGrip, GoLand, Rider, Cursive,
|
IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm, RubyMine, DataGrip, GoLand, Rider, Cursive,
|
||||||
Android Studio and other IntelliJ platform based IDEs.
|
Android Studio and other IntelliJ platform based IDEs.
|
||||||
|
|
||||||
Setup
|
Setup
|
||||||
@ -85,7 +85,7 @@ Here are some examples of supported vim features and commands:
|
|||||||
* Motion / deletion / change / window / etc. commands
|
* Motion / deletion / change / window / etc. commands
|
||||||
* Key mappings
|
* Key mappings
|
||||||
* Marks / Macros / Digraphs / Registers
|
* Marks / Macros / Digraphs / Registers
|
||||||
* Some [set commands](https://github.com/JetBrains/ideavim/wiki/%22set%22-commands)
|
* Some [set commands](https://github.com/JetBrains/ideavim/wiki/set-commands)
|
||||||
* Full Vim regexps for search and search/replace
|
* Full Vim regexps for search and search/replace
|
||||||
* Vim web help
|
* Vim web help
|
||||||
* `~/.ideavimrc` configuration file
|
* `~/.ideavimrc` configuration file
|
||||||
@ -309,7 +309,9 @@ endif
|
|||||||
|
|
||||||
The power of contributing drives IdeaVim :muscle:. Even small contributions matter!
|
The power of contributing drives IdeaVim :muscle:. Even small contributions matter!
|
||||||
|
|
||||||
See [CONTRIBUTING.md](CONTRIBUTING.md) to start bringing your value to the project.
|
See the contribution guide in [CONTRIBUTING.md](CONTRIBUTING.md) to start bringing your value to the project.
|
||||||
|
|
||||||
|
😎 In 2025, we launched a rewards program. See the guide for details.
|
||||||
|
|
||||||
Authors
|
Authors
|
||||||
-------
|
-------
|
||||||
@ -324,11 +326,11 @@ IdeaVim tips and tricks
|
|||||||
- `set ideajoin` to enable join via the IDE. See the [examples](https://jb.gg/f9zji9).
|
- `set ideajoin` to enable join via the IDE. See the [examples](https://jb.gg/f9zji9).
|
||||||
- Make sure `ideaput` is enabled for `clipboard` to enable native IJ insertion in Vim.
|
- Make sure `ideaput` is enabled for `clipboard` to enable native IJ insertion in Vim.
|
||||||
- Sync IJ bookmarks and IdeaVim global marks: `set ideamarks` (works for marks with capital letters only)
|
- Sync IJ bookmarks and IdeaVim global marks: `set ideamarks` (works for marks with capital letters only)
|
||||||
- Check out more [ex commands](https://github.com/JetBrains/ideavim/wiki/%22set%22-commands).
|
- Check out more [ex commands](https://github.com/JetBrains/ideavim/wiki/set-commands).
|
||||||
|
|
||||||
- Use your vim settings with IdeaVim. Put `source ~/.vimrc` in `~/.ideavimrc`.
|
- Use your vim settings with IdeaVim. Put `source ~/.vimrc` in `~/.ideavimrc`.
|
||||||
- Control the status bar icon via the [`ideastatusicon` option](https://github.com/JetBrains/ideavim/wiki/%22set%22-commands).
|
- Control the status bar icon via the [`ideastatusicon` option](https://github.com/JetBrains/ideavim/wiki/set-commands).
|
||||||
- Not familiar with the default behaviour during a refactoring? See the [`idearefactormode` option](https://github.com/JetBrains/ideavim/wiki/%22set%22-commands).
|
- Not familiar with the default behaviour during a refactoring? See the [`idearefactormode` option](https://github.com/JetBrains/ideavim/wiki/set-commands).
|
||||||
|
|
||||||
Some facts about Vim
|
Some facts about Vim
|
||||||
-------
|
-------
|
||||||
@ -370,6 +372,12 @@ is the full list of synonyms.
|
|||||||
|
|
||||||
- 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).
|
- 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).
|
||||||
|
|
||||||
|
- The Vim documentation contains many easter eggs. We encounter them occasionally, but GitHub user mikesmithgh has compiled a substantial collection [here](https://github.com/mikesmithgh/vimpromptu).
|
||||||
|
- In addition to `:call err_teapot()`, which returns `E418: I'm a teapot`, there is also `:call err_teapot(1)`, which returns `E503: Coffee is currently not available`. Naturally, this is also supported in IdeaVim.
|
||||||
|
|
||||||
|
- Insert mode has all `Ctrl` keys mapped, except `Ctrl-B`. In the documentation, it is marked as **"CTRL-B in Insert
|
||||||
|
mode gone"**. Call `:h i_CTRL-B-gone` in Vim to read why `Ctrl-B` was removed.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.serialization") version "2.0.0"
|
kotlin("plugin.serialization") version "2.0.21"
|
||||||
}
|
}
|
||||||
|
|
||||||
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:2.1.10-1.0.29")
|
||||||
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")
|
||||||
|
@ -45,7 +45,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21")
|
||||||
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")
|
||||||
|
|
||||||
@ -53,11 +53,11 @@ buildscript {
|
|||||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-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:3.1.1")
|
||||||
classpath("io.ktor:ktor-client-cio:3.0.2")
|
classpath("io.ktor:ktor-client-cio:3.1.1")
|
||||||
classpath("io.ktor:ktor-client-auth:3.0.2")
|
classpath("io.ktor:ktor-client-auth:3.1.1")
|
||||||
classpath("io.ktor:ktor-client-content-negotiation:3.0.2")
|
classpath("io.ktor:ktor-client-content-negotiation:3.1.1")
|
||||||
classpath("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
|
classpath("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
|
||||||
|
|
||||||
// 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")
|
||||||
@ -66,14 +66,14 @@ buildscript {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
kotlin("jvm") version "2.0.0"
|
kotlin("jvm") version "2.0.21"
|
||||||
application
|
application
|
||||||
id("java-test-fixtures")
|
id("java-test-fixtures")
|
||||||
id("org.jetbrains.intellij.platform") version "2.2.0"
|
id("org.jetbrains.intellij.platform") version "2.3.0"
|
||||||
id("org.jetbrains.changelog") version "2.2.1"
|
id("org.jetbrains.changelog") version "2.2.1"
|
||||||
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 "4.0.1"
|
||||||
id("com.google.devtools.ksp") version "2.0.0-1.0.23"
|
id("com.google.devtools.ksp") version "2.0.21-1.0.25"
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleSources by configurations.registering
|
val moduleSources by configurations.registering
|
||||||
@ -107,7 +107,7 @@ dependencies {
|
|||||||
compileOnly(project(":annotation-processors"))
|
compileOnly(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:26.0.2")
|
||||||
|
|
||||||
intellijPlatform {
|
intellijPlatform {
|
||||||
// Snapshots don't use installers
|
// Snapshots don't use installers
|
||||||
@ -121,25 +121,15 @@ dependencies {
|
|||||||
|
|
||||||
pluginVerifier()
|
pluginVerifier()
|
||||||
zipSigner()
|
zipSigner()
|
||||||
instrumentationTools()
|
|
||||||
|
|
||||||
testFramework(TestFrameworkType.Platform)
|
testFramework(TestFrameworkType.Platform)
|
||||||
testFramework(TestFrameworkType.JUnit5)
|
testFramework(TestFrameworkType.JUnit5)
|
||||||
|
|
||||||
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
|
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
|
||||||
plugin("AceJump", "3.8.19")
|
plugin("AceJump", "3.8.19")
|
||||||
|
plugin("com.intellij.classic.ui", "242.20224.159")
|
||||||
|
|
||||||
// [VERSION UPDATE] 2024.3+
|
bundledPlugins("org.jetbrains.plugins.terminal", "com.intellij.modules.json")
|
||||||
// Dirty hack: in 2024.3+ the json plugin is extracted. So, the tests that use JSON don't work.
|
|
||||||
// If the IdeaVim minimal version is bumped to 2024.3, leave only `bundledPlugins...` line
|
|
||||||
// If we start to support a new version like 2024.2.2 or 2025.1.1, please add it to the correct branch of this when
|
|
||||||
// This check intentionally has a pattern where we explicitly specify if to use the JSON plugin for each
|
|
||||||
// supported version or not to prevent developers from figuring out what happened with JSON.
|
|
||||||
when (ideaVersion) {
|
|
||||||
"2024.2.1" -> { /* Nothing */ }
|
|
||||||
"LATEST-EAP-SNAPSHOT", "2024.3" -> bundledPlugins("com.intellij.modules.json")
|
|
||||||
else -> error("Unsupported version: $ideaVersion")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
|
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
|
||||||
@ -161,17 +151,17 @@ dependencies {
|
|||||||
// 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.4.0")
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
|
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
|
testImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.12.0")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.12.0")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.12.0")
|
||||||
|
|
||||||
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
|
// 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
|
// Can be removed when IJPL-159134 is fixed
|
||||||
// testRuntimeOnly("junit:junit:4.13.2")
|
// testRuntimeOnly("junit:junit:4.13.2")
|
||||||
testImplementation("org.junit.vintage:junit-vintage-engine:5.10.5")
|
testImplementation("org.junit.vintage:junit-vintage-engine:5.12.0")
|
||||||
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
|
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +171,19 @@ configurations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val currentJavaVersion = javaToolchains.launcherFor {}.get().metadata.languageVersion.toString()
|
||||||
|
if (currentJavaVersion != javaVersion) {
|
||||||
|
// NOTE: I made this exception because the default Gradle error message is horrible, noone can understand it.
|
||||||
|
throw RuntimeException(
|
||||||
|
"""
|
||||||
|
Incorrect java version used for building.
|
||||||
|
IdeaVim uses java version $javaVersion, but the current java version is $currentJavaVersion.
|
||||||
|
If IntelliJ IDEA is used, change the setting in "Settings | Build, Execution, Deployment | Build Tools | Gradle"
|
||||||
|
If build is run from the terminal, set JAVA_HOME environment variable to the correct java version.
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
@ -194,6 +197,11 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false)
|
systemProperty("ideavim.nvim.test", System.getProperty("nvim") ?: false)
|
||||||
|
|
||||||
|
// This removes all localization plugins from the test version of IJ.
|
||||||
|
// There is a bug that IJ for tests may be loaded with a different locale and some keys may be missing there,
|
||||||
|
// what breaks the tests. This usually happens in EAP versions of IJ.
|
||||||
|
classpath -= classpath.filter { it.name.startsWith("localization-") && it.name.endsWith(".jar") }
|
||||||
}
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
@ -211,7 +219,7 @@ tasks {
|
|||||||
jvmTarget = javaVersion
|
jvmTarget = javaVersion
|
||||||
// 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 = "2.0"
|
||||||
freeCompilerArgs = listOf(
|
freeCompilerArgs = listOf(
|
||||||
"-Xjvm-default=all-compatibility",
|
"-Xjvm-default=all-compatibility",
|
||||||
|
|
||||||
@ -225,9 +233,11 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
|
enabled = false
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = javaVersion
|
jvmTarget = javaVersion
|
||||||
apiVersion = "1.9"
|
apiVersion = "2.0"
|
||||||
|
|
||||||
// Needed to compile the AceJump which uses kotlin beta
|
// Needed to compile the AceJump which uses kotlin beta
|
||||||
// Without these two option compilation fails
|
// Without these two option compilation fails
|
||||||
@ -324,9 +334,7 @@ intellijPlatform {
|
|||||||
name = "IdeaVim"
|
name = "IdeaVim"
|
||||||
changeNotes.set(
|
changeNotes.set(
|
||||||
"""
|
"""
|
||||||
Undo in IdeaVim now works like in Vim<br/>
|
We’ve launched a program to reward quality contributions with a one-year All Products Pack subscription. Learn more at: <a href="https://github.com/JetBrains/ideavim/blob/master/CONTRIBUTING.md">CONTRIBUTING.md</a> .
|
||||||
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/>
|
||||||
<br/>
|
<br/>
|
||||||
<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
|
<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
|
||||||
@ -837,7 +845,7 @@ fun updateAuthors(uncheckedEmails: Set<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun List<Author>.toMdString(): String {
|
fun List<Author>.toMdString(): String {
|
||||||
return this.joinToString {
|
return this.joinToString(separator = "") {
|
||||||
"""
|
"""
|
||||||
|
|
|
|
||||||
|* [![icon][mail]](mailto:${it.mail})
|
|* [![icon][mail]](mailto:${it.mail})
|
||||||
|
@ -423,6 +423,24 @@ https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary>
|
||||||
|
|
||||||
|
### Features:
|
||||||
|
Provides additional text object motions for handling quotes and brackets. The following motions are included:
|
||||||
|
|
||||||
|
- aq: Around any quotes.
|
||||||
|
- iq: Inside any quotes.
|
||||||
|
- ab: Around any parentheses, curly braces, and square brackets.
|
||||||
|
- ib: Inside any parentheses, curly braces, and square brackets.
|
||||||
|
|
||||||
|
Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai).
|
||||||
|
|
||||||
|
### Setup:
|
||||||
|
- Add the following command to `~/.ideavimrc`: `set mini-ai`
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><h2>Which-Key</h2></summary>
|
<summary><h2>Which-Key</h2></summary>
|
||||||
@ -484,4 +502,3 @@ or restart the IDE.
|
|||||||
|
|
||||||
https://plugins.jetbrains.com/plugin/25899-vim-switch
|
https://plugins.jetbrains.com/plugin/25899-vim-switch
|
||||||
|
|
||||||
</details>
|
|
||||||
|
44
doc/posts/Some-Facts-About-Vim.md
Normal file
44
doc/posts/Some-Facts-About-Vim.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Some facts about Vim
|
||||||
|
|
||||||
|
Let’s relax and have some fun now! Here are a few things we've found interesting during development
|
||||||
|
and would like to share with you.
|
||||||
|
|
||||||
|
- There are no such commands as `dd`, `yy`, or `cc`. For example, `dd` is not a separate command for deleting the line,
|
||||||
|
but a `d` command with a `d` motion.
|
||||||
|
Wait, but there isn't a `d` motion in Vim! That’s right, and that’s why Vim has a dedicated set of commands
|
||||||
|
for which it checks whether the
|
||||||
|
[command equals to motion](https://github.com/vim/vim/blob/759d81549c1340185f0d92524c563bb37697ea88/src/normal.c#L6468)
|
||||||
|
and if so, it executes `_` motion instead.
|
||||||
|
`_` is an interesting motion that isn't even documented in vi, and it refers to the current line.
|
||||||
|
So, commands like `dd`, `yy`, and similar ones are simply translated to `d_`, `y_`, etc.
|
||||||
|
[Here](https://github.com/vim/vim/blob/759d81549c1340185f0d92524c563bb37697ea88/src/normal.c#L6502)
|
||||||
|
is the source of this knowledge.
|
||||||
|
|
||||||
|
- `x`, `D`, and `&` are not separate commands either. They are synonyms of `dl`, `d$`, and `:s\r`, respectively.
|
||||||
|
[Here](https://github.com/vim/vim/blob/759d81549c1340185f0d92524c563bb37697ea88/src/normal.c#L5365)
|
||||||
|
is the full list of synonyms.
|
||||||
|
|
||||||
|
- You can read a [post](https://github.com/JetBrains/ideavim/wiki/how-many-modes-does-vim-have) about how modes work in Vim and IdeaVim.
|
||||||
|
|
||||||
|
- Have you ever used `U` after `dd`? [Don't even try](https://github.com/vim/vim/blob/759d81549c1340185f0d92524c563bb37697ea88/src/ops.c#L874).
|
||||||
|
|
||||||
|
- A lot of variables that refer to visual mode start with two uppercase letters, e.g. `VIsual_active`. [Some examples](https://github.com/vim/vim/blob/master/src/normal.c#L17).
|
||||||
|
As mentioned [here](https://vi.stackexchange.com/a/42885/12441), this was done this way to avoid the clash with X11.
|
||||||
|
|
||||||
|
- Other [strange things](https://github.com/vim/vim/blob/759d81549c1340185f0d92524c563bb37697ea88/src/ex_docmd.c#L1845) from vi:
|
||||||
|
* ":3" jumps to line 3
|
||||||
|
* ":3|..." prints line 3
|
||||||
|
* ":|" prints current line
|
||||||
|
|
||||||
|
- Vim script doesn't skip white space before comma. `F(a ,b)` => E475.
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- 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).
|
||||||
|
|
||||||
|
- The Vim documentation contains many easter eggs. We encounter them occasionally, but GitHub user mikesmithgh has compiled a substantial collection [here](https://github.com/mikesmithgh/vimpromptu).
|
||||||
|
- In addition to `:call err_teapot()`, which returns `E418: I'm a teapot`, there is also `:call err_teapot(1)`, which returns `E503: Coffee is currently not available`. Naturally, this is also supported in IdeaVim.
|
||||||
|
|
||||||
|
- Insert mode has all `Ctrl` keys mapped, except `Ctrl-B`. In the documentation, it is marked as **"CTRL-B in Insert
|
||||||
|
mode gone"**. Call `:h i_CTRL-B-gone` in Vim to read why `Ctrl-B` was removed.
|
@ -16,19 +16,19 @@
|
|||||||
# https://data.services.jetbrains.com/products?code=IC
|
# https://data.services.jetbrains.com/products?code=IC
|
||||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
||||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
||||||
ideaVersion=2024.2.1
|
ideaVersion=2024.3.3
|
||||||
# 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
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=SNAPSHOT
|
version=chylex-43
|
||||||
javaVersion=17
|
javaVersion=21
|
||||||
remoteRobotVersion=0.11.23
|
remoteRobotVersion=0.11.23
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
|
||||||
|
|
||||||
# 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=2.0.21
|
||||||
publishToken=token
|
publishToken=token
|
||||||
publishChannels=eap
|
publishChannels=eap
|
||||||
|
|
||||||
@ -41,7 +41,6 @@ youtrackToken=
|
|||||||
|
|
||||||
# Gradle settings
|
# Gradle settings
|
||||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||||
org.gradle.configuration-cache=true
|
|
||||||
org.gradle.caching=true
|
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
|
||||||
@ -49,3 +48,5 @@ kotlin.stdlib.default.dependency=false
|
|||||||
|
|
||||||
# Disable incremental annotation processing
|
# Disable incremental annotation processing
|
||||||
ksp.incremental=false
|
ksp.incremental=false
|
||||||
|
# KSP2 is used with java 21: https://github.com/google/ksp/issues/740#issuecomment-2313498615
|
||||||
|
ksp.useKSP2=true
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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.12.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
110555
qodana.sarif.json
110555
qodana.sarif.json
File diff suppressed because it is too large
Load Diff
30
qodana.yaml
30
qodana.yaml
@ -1,30 +0,0 @@
|
|||||||
version: 1.0
|
|
||||||
profile:
|
|
||||||
name: Qodana
|
|
||||||
include:
|
|
||||||
- name: CheckDependencyLicenses
|
|
||||||
exclude:
|
|
||||||
- name: MoveVariableDeclarationIntoWhen
|
|
||||||
- name: PluginXmlValidity
|
|
||||||
- name: RedundantThrows
|
|
||||||
- name: SuperTearDownInFinally
|
|
||||||
- name: UnusedReturnValue
|
|
||||||
- name: All
|
|
||||||
paths:
|
|
||||||
- build.gradle.kts
|
|
||||||
- gradle/wrapper/gradle-wrapper.properties
|
|
||||||
- src/main/resources/icons/youtrack.svg
|
|
||||||
- src/main/java/com/maddyhome/idea/vim/helper/SearchHelper.java
|
|
||||||
- src/main/java/com/maddyhome/idea/vim/regexp/RegExp.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/SimpleText.kt
|
|
||||||
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
|
||||||
- vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
|
|
||||||
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
|
|
||||||
- tests/ui-fixtures
|
|
||||||
dependencyIgnores:
|
|
||||||
- name: "acejump"
|
|
||||||
- name: "icu4j"
|
|
||||||
- name: "antlr-runtime"
|
|
||||||
- name: "javax.json"
|
|
@ -20,13 +20,13 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.0")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.10")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core:3.0.2")
|
implementation("io.ktor:ktor-client-core:3.1.1")
|
||||||
implementation("io.ktor:ktor-client-cio:3.0.2")
|
implementation("io.ktor:ktor-client-cio:3.1.1")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:3.0.2")
|
implementation("io.ktor:ktor-client-content-negotiation:3.1.1")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.1")
|
||||||
implementation("io.ktor:ktor-client-auth:3.0.2")
|
implementation("io.ktor:ktor-client-auth:3.1.1")
|
||||||
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
|
||||||
@ -107,13 +107,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")
|
||||||
|
@ -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()
|
|
||||||
}
|
|
@ -19,3 +19,4 @@ include("tests:long-running-tests")
|
|||||||
include("tests:ui-ij-tests")
|
include("tests:ui-ij-tests")
|
||||||
include("tests:ui-py-tests")
|
include("tests:ui-py-tests")
|
||||||
include("tests:ui-fixtures")
|
include("tests:ui-fixtures")
|
||||||
|
include("tests:ui-rd-tests")
|
||||||
|
@ -10,6 +10,7 @@ package com.maddyhome.idea.vim
|
|||||||
|
|
||||||
import com.intellij.ide.BrowserUtil
|
import com.intellij.ide.BrowserUtil
|
||||||
import com.intellij.ide.plugins.IdeaPluginDescriptor
|
import com.intellij.ide.plugins.IdeaPluginDescriptor
|
||||||
|
import com.intellij.ide.plugins.InstalledPluginsState
|
||||||
import com.intellij.ide.plugins.PluginStateListener
|
import com.intellij.ide.plugins.PluginStateListener
|
||||||
import com.intellij.ide.plugins.PluginStateManager
|
import com.intellij.ide.plugins.PluginStateManager
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
@ -21,6 +22,7 @@ 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.newapi.IjVimEditor
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimEnabler
|
||||||
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.initInjector
|
||||||
import com.maddyhome.idea.vim.ui.JoinEap
|
import com.maddyhome.idea.vim.ui.JoinEap
|
||||||
@ -40,22 +42,28 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
|||||||
if (firstInitializationOccurred) return
|
if (firstInitializationOccurred) return
|
||||||
firstInitializationOccurred = true
|
firstInitializationOccurred = true
|
||||||
|
|
||||||
if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
|
if (!VimPlugin.getVimState().wasSubscribedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
|
||||||
VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
|
VimPlugin.getVimState().wasSubscribedToEAPAutomatically = true
|
||||||
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
|
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
|
||||||
}
|
}
|
||||||
|
|
||||||
// This code should be executed once
|
// This code should be executed once
|
||||||
VimPlugin.getInstance().initialize()
|
VimPlugin.getInstance().initialize()
|
||||||
|
|
||||||
|
(injector.enabler as IjVimEnabler).ideOpened()
|
||||||
|
|
||||||
// Uninstall survey. Should be registered once for all projects
|
// Uninstall survey. Should be registered once for all projects
|
||||||
PluginStateManager.addStateListener(object : PluginStateListener {
|
PluginStateManager.addStateListener(object : PluginStateListener {
|
||||||
override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
|
override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun uninstall(descriptor: IdeaPluginDescriptor) {
|
override fun uninstall(descriptor: IdeaPluginDescriptor) {
|
||||||
if (descriptor.pluginId == VimPlugin.getPluginId()) {
|
val pluginId = VimPlugin.getPluginId()
|
||||||
BrowserUtil.open("https://surveys.jetbrains.com/s3/ideavim-uninstall-feedback")
|
// This event is called for both uninstall and update. There is no proper way to distinguish these two events.
|
||||||
|
// In order not to show the form for the update, we check if the new version is available. If so,
|
||||||
|
// this may be an update (and may not), and we don't show the form.
|
||||||
|
if (descriptor.pluginId == pluginId && !InstalledPluginsState.getInstance().hasNewerVersion(pluginId)) {
|
||||||
|
BrowserUtil.open("https://jb.gg/z6c7db")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -215,7 +215,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
|
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
|
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,12 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
|
|||||||
LOG.trace("Before execute for typed action")
|
LOG.trace("Before execute for typed action")
|
||||||
if (editor.isIdeaVimDisabledHere) {
|
if (editor.isIdeaVimDisabledHere) {
|
||||||
LOG.trace("IdeaVim disabled here, finish")
|
LOG.trace("IdeaVim disabled here, finish")
|
||||||
(KeyHandlerKeeper.getInstance().originalHandler as? TypedActionHandlerEx)?.beforeExecute(editor, charTyped, context, plan)
|
(KeyHandlerKeeper.getInstance().originalHandler as? TypedActionHandlerEx)?.beforeExecute(
|
||||||
|
editor,
|
||||||
|
charTyped,
|
||||||
|
context,
|
||||||
|
plan
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,13 +14,16 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
|
|||||||
import com.intellij.openapi.actionSystem.AnAction
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.AnActionWrapper
|
import com.intellij.openapi.actionSystem.AnActionWrapper
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
import com.intellij.openapi.application.invokeLater
|
import com.intellij.openapi.application.invokeLater
|
||||||
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.intellij.openapi.editor.impl.EditorComponentImpl
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
import com.intellij.openapi.project.DumbAware
|
import com.intellij.openapi.project.DumbAware
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.intellij.openapi.util.registry.Registry
|
||||||
import com.intellij.ui.KeyStrokeAdapter
|
import com.intellij.ui.KeyStrokeAdapter
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
@ -41,7 +44,6 @@ import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
|||||||
import com.maddyhome.idea.vim.key.ShortcutOwner
|
import com.maddyhome.idea.vim.key.ShortcutOwner
|
||||||
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
|
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.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.initInjector
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
@ -88,9 +90,9 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
||||||
if (start != null) {
|
if (start != null) {
|
||||||
val duration = System.currentTimeMillis() - start
|
val duration = System.currentTimeMillis() - start
|
||||||
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
LOG.info("VimShortcut execution '$keyStroke': $duration ms")
|
||||||
}
|
}
|
||||||
} catch (ignored: ProcessCanceledException) {
|
} catch (_: ProcessCanceledException) {
|
||||||
// Control-flow exceptions (like ProcessCanceledException) should never be logged
|
// Control-flow exceptions (like ProcessCanceledException) should never be logged
|
||||||
// See {@link com.intellij.openapi.diagnostic.Logger.checkException}
|
// See {@link com.intellij.openapi.diagnostic.Logger.checkException}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
@ -116,115 +118,123 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isEnabled(e: AnActionEvent, keyStroke: KeyStroke?): ActionEnableStatus {
|
private fun isEnabled(e: AnActionEvent, keyStroke: KeyStroke?): ActionEnableStatus {
|
||||||
|
if (keyStroke == null) return ActionEnableStatus.no("Keystroke is null", LogLevel.DEBUG)
|
||||||
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
|
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
|
||||||
val editor = getEditor(e)
|
val editor = getEditor(e) ?: return ActionEnableStatus.no("Can't get Editor", LogLevel.DEBUG)
|
||||||
if (editor != null && keyStroke != null) {
|
|
||||||
if (enableOctopus) {
|
|
||||||
if (isOctopusEnabled(keyStroke, editor)) {
|
|
||||||
return ActionEnableStatus.no(
|
|
||||||
"Processing VimShortcutKeyAction for the key that is used in the octopus handler",
|
|
||||||
LogLevel.ERROR
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (editor.isIdeaVimDisabledHere) {
|
|
||||||
return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO)
|
|
||||||
}
|
|
||||||
// Workaround for smart step into
|
|
||||||
@Suppress("DEPRECATION", "LocalVariableName", "VariableNaming")
|
|
||||||
val SMART_STEP_INPLACE_DATA = Key.findKeyByName("SMART_STEP_INPLACE_DATA")
|
|
||||||
if (SMART_STEP_INPLACE_DATA != null && editor.getUserData(SMART_STEP_INPLACE_DATA) != null) {
|
|
||||||
LOG.trace("Do not execute shortcut because of smart step")
|
|
||||||
return ActionEnableStatus.no("Smart step into is active", LogLevel.INFO)
|
|
||||||
}
|
|
||||||
|
|
||||||
val keyCode = keyStroke.keyCode
|
if (enableOctopus) {
|
||||||
|
if (isOctopusEnabled(keyStroke, editor)) {
|
||||||
if (HandlerInjector.notebookCommandMode(editor)) {
|
return ActionEnableStatus.no(
|
||||||
LOG.debug("Python Notebook command mode")
|
"Processing VimShortcutKeyAction for the key that is used in the octopus handler",
|
||||||
if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_KP_RIGHT || keyCode == KeyEvent.VK_ENTER) {
|
LogLevel.ERROR
|
||||||
invokeLater { editor.updateCaretsVisualAttributes() }
|
)
|
||||||
}
|
}
|
||||||
return ActionEnableStatus.no("Python notebook is in command mode", LogLevel.INFO)
|
}
|
||||||
}
|
|
||||||
|
if (e.dataContext.isNotSupportedContextComponent && Registry.`is`("ideavim.only.in.editor.component")) {
|
||||||
if (AceJumpService.getInstance()?.isActive(editor) == true) {
|
// Note: Currently, IdeaVim works ONLY in the editor & ExTextField component. However, the presence of the
|
||||||
return ActionEnableStatus.no("AceJump is active", LogLevel.INFO)
|
// PlatformDataKeys.EDITOR in the data context does not mean that the current focused component is editor.
|
||||||
}
|
// Note2: The registry key is needed for quick disabling in case something gets broken. It can be removed after
|
||||||
|
// some time if no issues are found.
|
||||||
if (LookupManager.getActiveLookup(editor) != null && !LookupKeys.isEnabledForLookup(keyStroke)) {
|
return ActionEnableStatus.no("Context component is not editor", LogLevel.INFO)
|
||||||
return ActionEnableStatus.no("Lookup keys are active", LogLevel.INFO)
|
}
|
||||||
}
|
|
||||||
|
if (editor.isIdeaVimDisabledHere) {
|
||||||
if (keyCode == KeyEvent.VK_ESCAPE) {
|
return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO)
|
||||||
return if (isEnabledForEscape(editor)) {
|
}
|
||||||
ActionEnableStatus.yes("Is enabled for Esc", LogLevel.INFO)
|
|
||||||
} else {
|
// Workaround for smart step into
|
||||||
ActionEnableStatus.no("Is disabled for Esc", LogLevel.INFO)
|
@Suppress("DEPRECATION", "LocalVariableName", "VariableNaming")
|
||||||
}
|
val SMART_STEP_INPLACE_DATA = Key.findKeyByName("SMART_STEP_INPLACE_DATA")
|
||||||
}
|
if (SMART_STEP_INPLACE_DATA != null && editor.getUserData(SMART_STEP_INPLACE_DATA) != null) {
|
||||||
|
LOG.trace("Do not execute shortcut because of smart step")
|
||||||
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) {
|
return ActionEnableStatus.no("Smart step into is active", LogLevel.INFO)
|
||||||
return ActionEnableStatus.no("The key is tab and the template is active", LogLevel.INFO)
|
}
|
||||||
}
|
|
||||||
|
val keyCode = keyStroke.keyCode
|
||||||
if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) {
|
|
||||||
return ActionEnableStatus.no("App code template is active", LogLevel.INFO)
|
if (HandlerInjector.notebookCommandMode(editor)) {
|
||||||
}
|
LOG.debug("Python Notebook command mode")
|
||||||
|
if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_KP_RIGHT || keyCode == KeyEvent.VK_ENTER) {
|
||||||
if (editor.inInsertMode) {
|
invokeLater { editor.updateCaretsVisualAttributes() }
|
||||||
if (keyCode == KeyEvent.VK_TAB) {
|
}
|
||||||
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
|
return ActionEnableStatus.no("Python notebook is in command mode", LogLevel.INFO)
|
||||||
// There are multiple actions registered for VK_TAB. The important items, in order, are this, the Live
|
}
|
||||||
// Templates action and TabAction. Returning false in insert mode means that the Live Template action gets to
|
|
||||||
// execute, and this allows Emmet to work (VIM-674). But it also means that the VimEditorTab handle is never
|
if (AceJumpService.getInstance()?.isActive(editor) == true) {
|
||||||
// called, so we can't scroll the caret into view correctly.
|
return ActionEnableStatus.no("AceJump is active", LogLevel.INFO)
|
||||||
// If we do return true, VimEditorTab handles the Vim side of things and then invokes
|
}
|
||||||
// IdeActions.ACTION_EDITOR_TAB, which inserts the tab. It also bypasses the Live Template action, and Emmet
|
|
||||||
// no longer works.
|
if (LookupManager.getActiveLookup(editor) != null && !LookupKeys.isEnabledForLookup(keyStroke)) {
|
||||||
// This flag is used when recording text entry/keystrokes for repeated insertion. Because we return false and
|
return ActionEnableStatus.no("Lookup keys are active", LogLevel.INFO)
|
||||||
// don't execute the VimEditorTab handler, we don't record tab as an action. Instead, we see an incoming text
|
}
|
||||||
// change of multiple whitespace characters, which is normally ignored because it's auto-indent content from
|
|
||||||
// hitting <Enter>. When this flag is set, we record the whitespace as the output of the <Tab>
|
if (keyCode == KeyEvent.VK_ESCAPE) {
|
||||||
VimPlugin.getChange().tabAction = true
|
return if (isEnabledForEscape(editor)) {
|
||||||
return ActionEnableStatus.no("Tab action in insert mode", LogLevel.INFO)
|
ActionEnableStatus.yes("Is enabled for Esc", LogLevel.INFO)
|
||||||
}
|
} else {
|
||||||
// Debug watch, Python console, etc.
|
ActionEnableStatus.no("Is disabled for Esc", LogLevel.INFO)
|
||||||
if (keyStroke in NON_FILE_EDITOR_KEYS && !EditorHelper.isFileEditor(editor)) {
|
}
|
||||||
return ActionEnableStatus.no("Non file editor keys", LogLevel.INFO)
|
}
|
||||||
}
|
|
||||||
}
|
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) {
|
||||||
|
return ActionEnableStatus.no("The key is tab and the template is active", LogLevel.INFO)
|
||||||
if (keyStroke in VIM_ONLY_EDITOR_KEYS) {
|
}
|
||||||
return ActionEnableStatus.yes("Vim only editor keys", LogLevel.INFO)
|
|
||||||
}
|
if (editor.inInsertMode) {
|
||||||
|
if (keyCode == KeyEvent.VK_TAB) {
|
||||||
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
|
// TODO: This stops VimEditorTab seeing <Tab> in insert mode and correctly scrolling the view
|
||||||
val info = savedShortcutConflicts[keyStroke]
|
// There are multiple actions registered for VK_TAB. The important items, in order, are this, the Live
|
||||||
return when (info?.forEditor(editor.vim)) {
|
// Templates action and TabAction. Returning false in insert mode means that the Live Template action gets to
|
||||||
ShortcutOwner.VIM -> {
|
// execute, and this allows Emmet to work (VIM-674). But it also means that the VimEditorTab handle is never
|
||||||
return ActionEnableStatus.yes("Owner is vim", LogLevel.DEBUG)
|
// called, so we can't scroll the caret into view correctly.
|
||||||
}
|
// If we do return true, VimEditorTab handles the Vim side of things and then invokes
|
||||||
ShortcutOwner.IDE -> {
|
// IdeActions.ACTION_EDITOR_TAB, which inserts the tab. It also bypasses the Live Template action, and Emmet
|
||||||
if (!isShortcutConflict(keyStroke)) {
|
// no longer works.
|
||||||
ActionEnableStatus.yes("Owner is IDE, but no actionve shortcut conflict", LogLevel.DEBUG)
|
// This flag is used when recording text entry/keystrokes for repeated insertion. Because we return false and
|
||||||
} else {
|
// don't execute the VimEditorTab handler, we don't record tab as an action. Instead, we see an incoming text
|
||||||
ActionEnableStatus.no("Owner is IDE", LogLevel.DEBUG)
|
// change of multiple whitespace characters, which is normally ignored because it's auto-indent content from
|
||||||
}
|
// hitting <Enter>. When this flag is set, we record the whitespace as the output of the <Tab>
|
||||||
}
|
VimPlugin.getChange().tabAction = true
|
||||||
else -> {
|
return ActionEnableStatus.no("Tab action in insert mode", LogLevel.INFO)
|
||||||
if (isShortcutConflict(keyStroke)) {
|
}
|
||||||
savedShortcutConflicts[keyStroke] = ShortcutOwnerInfo.allUndefined
|
// Debug watch, Python console, etc.
|
||||||
}
|
if (keyStroke in NON_FILE_EDITOR_KEYS && !EditorHelper.isFileEditor(editor)) {
|
||||||
ActionEnableStatus.yes("Enable vim for shortcut without owner", LogLevel.DEBUG)
|
return ActionEnableStatus.no("Non file editor keys", LogLevel.INFO)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyStroke in VIM_ONLY_EDITOR_KEYS) {
|
||||||
|
return ActionEnableStatus.yes("Vim only editor keys", LogLevel.INFO)
|
||||||
|
}
|
||||||
|
|
||||||
|
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
|
||||||
|
val info = savedShortcutConflicts[keyStroke]
|
||||||
|
return when (info?.forEditor(editor.vim)) {
|
||||||
|
ShortcutOwner.VIM -> {
|
||||||
|
ActionEnableStatus.yes("Owner is vim", LogLevel.DEBUG)
|
||||||
|
}
|
||||||
|
|
||||||
|
ShortcutOwner.IDE -> {
|
||||||
|
if (!isShortcutConflict(keyStroke)) {
|
||||||
|
ActionEnableStatus.yes("Owner is IDE, but no actionve shortcut conflict", LogLevel.DEBUG)
|
||||||
|
} else {
|
||||||
|
ActionEnableStatus.no("Owner is IDE", LogLevel.DEBUG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
if (isShortcutConflict(keyStroke)) {
|
||||||
|
savedShortcutConflicts[keyStroke] = ShortcutOwnerInfo.allUndefined
|
||||||
|
}
|
||||||
|
ActionEnableStatus.yes("Enable vim for shortcut without owner", LogLevel.DEBUG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ActionEnableStatus.no("End of the selection", LogLevel.DEBUG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isEnabledForEscape(editor: Editor): Boolean {
|
private fun isEnabledForEscape(editor: Editor): Boolean {
|
||||||
val ideaVimSupportDialog = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
val ideaVimSupportDialog =
|
||||||
|
injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||||
return editor.isPrimaryEditor() ||
|
return editor.isPrimaryEditor() ||
|
||||||
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
|
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
|
||||||
ideaVimSupportDialog && !editor.vim.mode.inNormalMode
|
ideaVimSupportDialog && !editor.vim.mode.inNormalMode
|
||||||
@ -387,4 +397,10 @@ private class ActionEnableStatus(
|
|||||||
|
|
||||||
private enum class LogLevel {
|
private enum class LogLevel {
|
||||||
DEBUG, INFO, ERROR,
|
DEBUG, INFO, ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val DataContext.isNotSupportedContextComponent: Boolean
|
||||||
|
get() {
|
||||||
|
val contextComponent = this.getData(PlatformDataKeys.CONTEXT_COMPONENT) ?: return true
|
||||||
|
return contextComponent !is EditorComponentImpl && contextComponent !is ExTextField
|
||||||
|
}
|
||||||
|
@ -37,7 +37,12 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
|||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
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,
|
||||||
|
motionType: SelectionType,
|
||||||
|
): Boolean {
|
||||||
val func = injector.globalOptions().operatorfunc
|
val func = injector.globalOptions().operatorfunc
|
||||||
if (func.isEmpty()) {
|
if (func.isEmpty()) {
|
||||||
VimPlugin.showMessage(MessageHelper.message("E774"))
|
VimPlugin.showMessage(MessageHelper.message("E774"))
|
||||||
@ -61,8 +66,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
|
|||||||
// Get the argument for function('...') or funcref('...') for the error message
|
// Get the argument for function('...') or funcref('...') for the error message
|
||||||
val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
|
val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
|
||||||
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
func
|
func
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +104,12 @@ 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 as? Argument.Motion ?: return false
|
||||||
if (!editor.inRepeatMode) {
|
if (!editor.inRepeatMode) {
|
||||||
argumentCaptured = argument
|
argumentCaptured = argument
|
||||||
|
@ -23,7 +23,12 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
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 = injector.vimState
|
||||||
var lastCommand = VimRepeater.lastChangeCommand
|
var lastCommand = VimRepeater.lastChangeCommand
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CA
|
|||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
operatorArguments: OperatorArguments
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val undo = injector.undo
|
val undo = injector.undo
|
||||||
when (undo) {
|
when (undo) {
|
||||||
@ -67,7 +67,7 @@ internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARE
|
|||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
operatorArguments: OperatorArguments
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val undo = injector.undo
|
val undo = injector.undo
|
||||||
when (undo) {
|
when (undo) {
|
||||||
@ -85,7 +85,12 @@ internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARE
|
|||||||
internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
|
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(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,14 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||||
|
import org.jetbrains.annotations.ApiStatus
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* COMPATIBILITY-LAYER: Additional class
|
* COMPATIBILITY-LAYER: Additional class
|
||||||
* Please see: https://jb.gg/zo8n0r
|
* Please see: https://jb.gg/zo8n0r
|
||||||
*/
|
*/
|
||||||
@Deprecated("Use `injector.vimState`")
|
@Deprecated("Use `injector.vimState`")
|
||||||
|
@ApiStatus.ScheduledForRemoval
|
||||||
class CommandState(private val machine: VimStateMachine) {
|
class CommandState(private val machine: VimStateMachine) {
|
||||||
|
|
||||||
val mode: Mode
|
val mode: Mode
|
||||||
@ -34,18 +36,21 @@ class CommandState(private val machine: VimStateMachine) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
|
@get:Deprecated(
|
||||||
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
|
"Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
|
||||||
"com.maddyhome.idea.vim.KeyHandler"
|
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
|
||||||
)
|
"com.maddyhome.idea.vim.KeyHandler"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
@get:ApiStatus.ScheduledForRemoval
|
||||||
val commandBuilder: CommandBuilder
|
val commandBuilder: CommandBuilder
|
||||||
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
|
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
|
||||||
|
|
||||||
@Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
|
@Deprecated(
|
||||||
"KeyHandler.getInstance().keyHandlerState.mappingState",
|
"Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
|
||||||
"com.maddyhome.idea.vim.KeyHandler"
|
"KeyHandler.getInstance().keyHandlerState.mappingState",
|
||||||
)
|
"com.maddyhome.idea.vim.KeyHandler"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val mappingState: MappingState
|
val mappingState: MappingState
|
||||||
get() = KeyHandler.getInstance().keyHandlerState.mappingState
|
get() = KeyHandler.getInstance().keyHandlerState.mappingState
|
||||||
@ -65,6 +70,7 @@ class CommandState(private val machine: VimStateMachine) {
|
|||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use `injector.vimState`")
|
@Deprecated("Use `injector.vimState`")
|
||||||
|
@ApiStatus.ScheduledForRemoval
|
||||||
fun getInstance(editor: Editor): CommandState {
|
fun getInstance(editor: Editor): CommandState {
|
||||||
return CommandState(injector.vimState)
|
return CommandState(injector.vimState)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ 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
|
import com.maddyhome.idea.vim.api.VimIndentConfig
|
||||||
|
|
||||||
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
|
internal class IndentConfig private constructor(indentOptions: IndentOptions) : VimIndentConfig {
|
||||||
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
|
||||||
|
@ -18,8 +18,8 @@ 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 wasSubscribedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
|
||||||
var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
|
var firstIdeaVimVersion: String? by StringProperty("first-ideavim-version", null)
|
||||||
|
|
||||||
fun readData(element: Element) {
|
fun readData(element: Element) {
|
||||||
val notifications = element.getChild("notifications")
|
val notifications = element.getChild("notifications")
|
||||||
@ -28,6 +28,11 @@ internal class VimState {
|
|||||||
map[name] = it.toBoolean()
|
map[name] = it.toBoolean()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stringMap.keys.forEach { name ->
|
||||||
|
notifications?.getChild(name)?.getAttributeValue("value")?.let {
|
||||||
|
stringMap[name] = it.decode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveData(element: Element) {
|
fun saveData(element: Element) {
|
||||||
@ -39,10 +44,22 @@ internal class VimState {
|
|||||||
child.setAttribute("enabled", value.toString())
|
child.setAttribute("enabled", value.toString())
|
||||||
notifications.addContent(child)
|
notifications.addContent(child)
|
||||||
}
|
}
|
||||||
|
stringMap.forEach { (name, value) ->
|
||||||
|
val child = Element(name)
|
||||||
|
child.setAttribute("value", value.encode)
|
||||||
|
notifications.addContent(child)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val String?.encode: String get() = this ?: NULL_VALUE
|
||||||
|
private val String?.decode: String? get() = if (this == NULL_VALUE) null else this
|
||||||
|
|
||||||
|
// Settings cannot store null values
|
||||||
|
private const val NULL_VALUE = "__NULL_VALUE_CONST__"
|
||||||
|
|
||||||
private val map by lazy { mutableMapOf<String, Boolean>() }
|
private val map by lazy { mutableMapOf<String, Boolean>() }
|
||||||
|
private val stringMap by lazy { mutableMapOf<String, String?>() }
|
||||||
|
|
||||||
private class StateProperty(val xmlName: String) : ReadWriteProperty<VimState, Boolean> {
|
private class StateProperty(val xmlName: String) : ReadWriteProperty<VimState, Boolean> {
|
||||||
|
|
||||||
@ -56,3 +73,19 @@ private class StateProperty(val xmlName: String) : ReadWriteProperty<VimState, B
|
|||||||
map[xmlName] = value
|
map[xmlName] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class StringProperty(val propertyName: String, val defaultValue: String?) :
|
||||||
|
ReadWriteProperty<VimState, String?> {
|
||||||
|
|
||||||
|
init {
|
||||||
|
stringMap[propertyName] = defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getValue(thisRef: VimState, property: KProperty<*>): String? {
|
||||||
|
return stringMap.getOrPut(propertyName) { defaultValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: VimState, property: KProperty<*>, value: String?) {
|
||||||
|
stringMap[propertyName] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2025 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.customization.feature.terminal
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.key.IdeaVimDisablerExtensionPoint
|
||||||
|
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isAlternateBufferEditor
|
||||||
|
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isOutputEditor
|
||||||
|
import org.jetbrains.plugins.terminal.block.util.TerminalDataContextUtils.isPromptEditor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The only implementation is defined right here.
|
||||||
|
*/
|
||||||
|
// [VERSION UPDATE] 2025.1+ Add 2 new predicates
|
||||||
|
internal class IdeaVimTerminalDisablerExtension : IdeaVimDisablerExtensionPoint {
|
||||||
|
override fun isDisabledForEditor(editor: Editor): Boolean {
|
||||||
|
return editor.isPromptEditor || editor.isOutputEditor || editor.isAlternateBufferEditor
|
||||||
|
// || editor.isOutputModelEditor || editor.isAlternateBufferModelEditor
|
||||||
|
}
|
||||||
|
}
|
@ -127,8 +127,7 @@ class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPane
|
|||||||
override fun close() {
|
override fun close() {
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
editor?.let { ExOutputPanel.getInstance(it).close() }
|
editor?.let { ExOutputPanel.getInstance(it).close() }
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
isActiveInTestMode = false
|
isActiveInTestMode = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public interface VimExtension {
|
|||||||
* let g:exchange_no_mappings=1
|
* 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
|
* 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.
|
* immediately, this variable won't be initialized at the moment of plugin initialization.
|
||||||
* <p>
|
* <p>
|
||||||
* There is also a tricky case for mappings override:
|
* There is also a tricky case for mappings override:
|
||||||
* ```
|
* ```
|
||||||
@ -48,8 +48,8 @@ public interface VimExtension {
|
|||||||
* map X <Plug>(ExchangeLine)
|
* map X <Plug>(ExchangeLine)
|
||||||
* ```
|
* ```
|
||||||
* For this case, a plugin with a good implementation detects that there is already a defined mapping for
|
* 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
|
* `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
|
||||||
* to be defined before the plugin initialization.
|
* to be defined before the plugin initialization.
|
||||||
*/
|
*/
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
|
@ -69,7 +69,8 @@ object VimExtensionFacade {
|
|||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
@Deprecated(
|
||||||
|
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||||
ReplaceWith(
|
ReplaceWith(
|
||||||
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||||
"com.maddyhome.idea.vim.VimPlugin"
|
"com.maddyhome.idea.vim.VimPlugin"
|
||||||
@ -186,24 +187,22 @@ object VimExtensionFacade {
|
|||||||
return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
|
||||||
@JvmStatic
|
|
||||||
@Deprecated("Please use com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister(com.maddyhome.idea.vim.api.VimEditor, char)")
|
|
||||||
fun getRegister(register: Char): List<KeyStroke>? {
|
|
||||||
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
|
||||||
return reg.keys
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getRegister(editor: VimEditor, register: Char): List<KeyStroke>? {
|
fun getRegister(editor: VimEditor, register: Char): List<KeyStroke>? {
|
||||||
val reg = VimPlugin.getRegister().getRegister(editor, injector.executionContextManager.getEditorExecutionContext(editor), register) ?: return null
|
val reg = VimPlugin.getRegister()
|
||||||
|
.getRegister(editor, injector.executionContextManager.getEditorExecutionContext(editor), register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
fun getRegisterForCaret(
|
||||||
val reg = caret.registerStorage.getRegister(register) ?: return null
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
|
register: Char,
|
||||||
|
caret: VimCaret,
|
||||||
|
): List<KeyStroke>? {
|
||||||
|
val reg = caret.registerStorage.getRegister(editor, context, register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +232,7 @@ object VimExtensionFacade {
|
|||||||
defaultArgs: List<Pair<String, Expression>>,
|
defaultArgs: List<Pair<String, Expression>>,
|
||||||
hasOptionalArguments: Boolean,
|
hasOptionalArguments: Boolean,
|
||||||
flags: EnumSet<FunctionFlag>,
|
flags: EnumSet<FunctionFlag>,
|
||||||
function: ScriptFunction
|
function: ScriptFunction,
|
||||||
) {
|
) {
|
||||||
var functionDeclaration: FunctionDeclaration? = null
|
var functionDeclaration: FunctionDeclaration? = null
|
||||||
val body = listOf(object : Executable {
|
val body = listOf(object : Executable {
|
||||||
@ -263,8 +262,7 @@ object VimExtensionFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||||
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) { editor, context, args ->
|
||||||
editor, context, args ->
|
|
||||||
|
|
||||||
val type = args["type"]?.asString()
|
val type = args["type"]?.asString()
|
||||||
val selectionType = when (type) {
|
val selectionType = when (type) {
|
||||||
@ -276,8 +274,7 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu
|
|||||||
|
|
||||||
if (function.apply(editor, context, selectionType)) {
|
if (function.apply(editor, context, selectionType)) {
|
||||||
ExecutionResult.Success
|
ExecutionResult.Success
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ExecutionResult.Error
|
ExecutionResult.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,4 +282,4 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu
|
|||||||
|
|
||||||
fun interface ScriptFunction {
|
fun interface ScriptFunction {
|
||||||
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@ import com.intellij.openapi.editor.Document;
|
|||||||
import com.maddyhome.idea.vim.KeyHandler;
|
import com.maddyhome.idea.vim.KeyHandler;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.MappingMode;
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments;
|
||||||
|
import com.maddyhome.idea.vim.command.TextObjectVisualType;
|
||||||
import com.maddyhome.idea.vim.common.TextRange;
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension;
|
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||||
|
@ -9,7 +9,6 @@ package com.maddyhome.idea.vim.extension.commentary
|
|||||||
|
|
||||||
import com.intellij.codeInsight.actions.AsyncActionExecutionService
|
import com.intellij.codeInsight.actions.AsyncActionExecutionService
|
||||||
import com.intellij.openapi.actionSystem.IdeActions
|
import com.intellij.openapi.actionSystem.IdeActions
|
||||||
import com.intellij.openapi.application.runWriteAction
|
|
||||||
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.openapi.util.Ref
|
import com.intellij.openapi.util.Ref
|
||||||
@ -65,18 +64,16 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
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. Fall back 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 {
|
listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE)
|
||||||
listOf(IdeActions.ACTION_COMMENT_BLOCK, IdeActions.ACTION_COMMENT_LINE)
|
|
||||||
}
|
|
||||||
|
|
||||||
val project = editor.ij.project!!
|
|
||||||
val callback = { afterCommenting(mode, editor, resetCaret, range) }
|
|
||||||
actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val project = editor.ij.project!!
|
||||||
|
val callback = { afterCommenting(mode, editor, resetCaret, range) }
|
||||||
|
return actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeActionWithCallbackOnSuccess(
|
private fun executeActionWithCallbackOnSuccess(
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.extension.exchange
|
package com.maddyhome.idea.vim.extension.exchange
|
||||||
|
|
||||||
import com.intellij.openapi.application.runWriteAction
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.LogicalPosition
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
import com.intellij.openapi.editor.colors.EditorColors
|
import com.intellij.openapi.editor.colors.EditorColors
|
||||||
@ -111,12 +110,10 @@ internal class VimExchangeExtension : VimExtension {
|
|||||||
|
|
||||||
private class VExchangeHandler : ExtensionHandler {
|
private class VExchangeHandler : ExtensionHandler {
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
runWriteAction {
|
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 ?: SelectionType.CHARACTER_WISE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,23 +223,22 @@ internal class VimExchangeExtension : VimExtension {
|
|||||||
val unnRegText = getRegister(editor.vim, '"')
|
val unnRegText = getRegister(editor.vim, '"')
|
||||||
val startRegText = getRegister(editor.vim, '*')
|
val startRegText = getRegister(editor.vim, '*')
|
||||||
val plusRegText = getRegister(editor.vim, '+')
|
val plusRegText = getRegister(editor.vim, '+')
|
||||||
runWriteAction {
|
|
||||||
// TODO handle:
|
// TODO handle:
|
||||||
// " Compare using =~ because "'==' != 0" returns 0
|
// " Compare using =~ because "'==' != 0" returns 0
|
||||||
// let indent = s:get_setting('exchange_indent', 1) !~ 0 && a:x.type ==# 'V' && a:y.type ==# 'V'
|
// let indent = s:get_setting('exchange_indent', 1) !~ 0 && a:x.type ==# 'V' && a:y.type ==# 'V'
|
||||||
pasteExchange(ex1, ex2)
|
pasteExchange(ex1, ex2)
|
||||||
if (!expand) {
|
if (!expand) {
|
||||||
pasteExchange(ex2, ex1)
|
pasteExchange(ex2, ex1)
|
||||||
}
|
|
||||||
// TODO: handle: if ident
|
|
||||||
if (!expand) {
|
|
||||||
fixCursor(ex1, ex2, reverse)
|
|
||||||
}
|
|
||||||
setRegister('z', zRegText)
|
|
||||||
setRegister('"', unnRegText)
|
|
||||||
setRegister('*', startRegText)
|
|
||||||
setRegister('+', plusRegText)
|
|
||||||
}
|
}
|
||||||
|
// TODO: handle: if ident
|
||||||
|
if (!expand) {
|
||||||
|
fixCursor(ex1, ex2, reverse)
|
||||||
|
}
|
||||||
|
setRegister('z', zRegText)
|
||||||
|
setRegister('"', unnRegText)
|
||||||
|
setRegister('*', startRegText)
|
||||||
|
setRegister('+', plusRegText)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult {
|
private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult {
|
||||||
|
@ -21,9 +21,7 @@ import com.intellij.openapi.editor.markup.TextAttributes
|
|||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import com.intellij.util.Alarm
|
import com.intellij.util.Alarm
|
||||||
import com.intellij.util.Alarm.ThreadToUse
|
import com.intellij.util.Alarm.ThreadToUse
|
||||||
import com.jetbrains.rd.util.first
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.ModeChangeListener
|
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||||
@ -123,9 +121,9 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
|
|||||||
initialised = false
|
initialised = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) {
|
override fun yankPerformed(editor: VimEditor, range: TextRange) {
|
||||||
ensureInitialised()
|
ensureInitialised()
|
||||||
highlightHandler.highlightYankRange(caretToRange)
|
highlightHandler.highlightYankRange(editor.ij, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
||||||
@ -146,25 +144,22 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
|
|||||||
private var lastEditor: Editor? = null
|
private var lastEditor: Editor? = null
|
||||||
private val highlighters = mutableSetOf<RangeHighlighter>()
|
private val highlighters = mutableSetOf<RangeHighlighter>()
|
||||||
|
|
||||||
fun highlightYankRange(caretToRange: Map<ImmutableVimCaret, TextRange>) {
|
fun highlightYankRange(editor: Editor, range: TextRange) {
|
||||||
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
|
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
|
||||||
clearYankHighlighters()
|
clearYankHighlighters()
|
||||||
|
|
||||||
val editor = caretToRange.first().key.editor.ij
|
|
||||||
lastEditor = editor
|
lastEditor = editor
|
||||||
|
|
||||||
val attributes = getHighlightTextAttributes(editor)
|
val attributes = getHighlightTextAttributes(editor)
|
||||||
for (range in caretToRange.values) {
|
for (i in 0 until range.size()) {
|
||||||
for (i in 0 until range.size()) {
|
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
range.startOffsets[i],
|
||||||
range.startOffsets[i],
|
range.endOffsets[i],
|
||||||
range.endOffsets[i],
|
HighlighterLayer.SELECTION,
|
||||||
HighlighterLayer.SELECTION,
|
attributes,
|
||||||
attributes,
|
HighlighterTargetArea.EXACT_RANGE,
|
||||||
HighlighterTargetArea.EXACT_RANGE,
|
)
|
||||||
)
|
highlighters.add(highlighter)
|
||||||
highlighters.add(highlighter)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
|
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
|
||||||
@ -282,4 +277,4 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
|
|||||||
return default
|
return default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ private object FileTypePatterns {
|
|||||||
} else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
|
} else if (fileTypeName == "CMakeLists.txt" || fileName == "CMakeLists") {
|
||||||
this.cMakePatterns
|
this.cMakePatterns
|
||||||
} else {
|
} else {
|
||||||
return null
|
this.htmlPatterns
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
290
src/main/java/com/maddyhome/idea/vim/extension/miniai/MiniAI.kt
Normal file
290
src/main/java/com/maddyhome/idea/vim/extension/miniai/MiniAI.kt
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2025 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.extension.miniai
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
|
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||||
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
|
import java.util.EnumSet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simplified imitation of mini.ai approach for motions "aq", "iq", "ab", "ib".
|
||||||
|
* Instead of "closest" text object, we apply a "cover-or-next" logic:
|
||||||
|
*
|
||||||
|
* 1) Among all candidate pairs, pick any that cover the caret (start <= caret < end).
|
||||||
|
* Among those, pick the smallest range.
|
||||||
|
* 2) If none cover the caret, pick the "next" pair (start >= caret) that is closest.
|
||||||
|
* 3) If none are next, pick the "previous" pair (end <= caret) that is closest.
|
||||||
|
*
|
||||||
|
* For "i" text object, we shrink the boundaries inward by one character on each side.
|
||||||
|
*/
|
||||||
|
class MiniAI : VimExtension {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
// <Plug> mappings
|
||||||
|
private const val PLUG_AQ = "<Plug>mini-ai-aq"
|
||||||
|
private const val PLUG_IQ = "<Plug>mini-ai-iq"
|
||||||
|
private const val PLUG_AB = "<Plug>mini-ai-ab"
|
||||||
|
private const val PLUG_IB = "<Plug>mini-ai-ib"
|
||||||
|
|
||||||
|
// Actual user key sequences
|
||||||
|
private const val KEY_AQ = "aq"
|
||||||
|
private const val KEY_IQ = "iq"
|
||||||
|
private const val KEY_AB = "ab"
|
||||||
|
private const val KEY_IB = "ib"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getName() = "mini-ai"
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
registerMappings()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerMappings() {
|
||||||
|
fun createHandler(
|
||||||
|
rangeFunc: (VimEditor, ImmutableVimCaret, Boolean) -> TextRange?
|
||||||
|
): ExtensionHandler = object : ExtensionHandler {
|
||||||
|
override val isRepeatable = true
|
||||||
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
|
addAction(PortedMiniAiAction(rangeFunc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listOf(
|
||||||
|
// Outer quotes
|
||||||
|
PLUG_AQ to createHandler { e, c, _ -> findQuoteRange(e, c, isOuter = true) },
|
||||||
|
// Inner quotes
|
||||||
|
PLUG_IQ to createHandler { e, c, _ -> findQuoteRange(e, c, isOuter = false) },
|
||||||
|
// Outer brackets
|
||||||
|
PLUG_AB to createHandler { e, c, _ -> findBracketRange(e, c, isOuter = true) },
|
||||||
|
// Inner brackets
|
||||||
|
PLUG_IB to createHandler { e, c, _ -> findBracketRange(e, c, isOuter = false) }
|
||||||
|
).forEach { (plug, handler) ->
|
||||||
|
putExtensionHandlerMapping(MappingMode.XO, injector.parser.parseKeys(plug), owner, handler, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map user keys -> <Plug> keys
|
||||||
|
listOf(
|
||||||
|
KEY_AQ to PLUG_AQ,
|
||||||
|
KEY_IQ to PLUG_IQ,
|
||||||
|
KEY_AB to PLUG_AB,
|
||||||
|
KEY_IB to PLUG_IB
|
||||||
|
).forEach { (key, plug) ->
|
||||||
|
putKeyMapping(MappingMode.XO, injector.parser.parseKeys(key), owner, injector.parser.parseKeys(plug), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A text object action that uses the "mini.ai-like" picking strategy.
|
||||||
|
private class PortedMiniAiAction(
|
||||||
|
private val rangeFunc: (VimEditor, ImmutableVimCaret, Boolean) -> TextRange?
|
||||||
|
) : TextObjectActionHandler() {
|
||||||
|
|
||||||
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK)
|
||||||
|
override val visualType: TextObjectVisualType = TextObjectVisualType.CHARACTER_WISE
|
||||||
|
|
||||||
|
override fun getRange(
|
||||||
|
editor: VimEditor,
|
||||||
|
caret: ImmutableVimCaret,
|
||||||
|
context: ExecutionContext,
|
||||||
|
count: Int,
|
||||||
|
rawCount: Int
|
||||||
|
): TextRange? = rangeFunc(editor, caret, true).also {
|
||||||
|
// We don't do 'count' expansions here. If you wanted to replicate
|
||||||
|
// mini.ai's multi-level expansions, you'd call the "rangeFunc" multiple
|
||||||
|
// times re-feeding the last output as reference, etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility to register action in KeyHandler
|
||||||
|
private fun addAction(action: TextObjectActionHandler) {
|
||||||
|
KeyHandler.getInstance().keyHandlerState.commandBuilder.addAction(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
* Core mini.ai-like logic
|
||||||
|
* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a text range for quotes (", ', `) around the caret using a "cover-or-next" approach.
|
||||||
|
* - If one or more pairs **cover** the caret, pick the *smallest* covering pair.
|
||||||
|
* - Else pick the "next" pair whose start >= caret offset (closest).
|
||||||
|
* - Else pick the "previous" pair whose end <= caret offset (closest).
|
||||||
|
*
|
||||||
|
* If [isOuter] == false (i.e. 'iq'), shrink the final range by 1 char on each side.
|
||||||
|
*/
|
||||||
|
private fun findQuoteRange(editor: VimEditor, caret: ImmutableVimCaret, isOuter: Boolean): TextRange? {
|
||||||
|
val text = editor.text()
|
||||||
|
val caretOffset = caret.offset
|
||||||
|
val caretLine = caret.getLine()
|
||||||
|
|
||||||
|
// 1) Gather quotes in *this caret's line*
|
||||||
|
val lineStart = editor.getLineStartOffset(caretLine)
|
||||||
|
val lineEnd = editor.getLineEndOffset(caretLine)
|
||||||
|
val lineText = text.substring(lineStart, lineEnd)
|
||||||
|
val lineRanges = gatherAllQuoteRanges(lineText).map {
|
||||||
|
TextRange(it.startOffset + lineStart, it.endOffset + lineStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
val localBest = pickBestRange(lineRanges, caretOffset)
|
||||||
|
if (localBest != null) {
|
||||||
|
return adjustRangeForInnerOuter(localBest, isOuter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Fallback: entire buffer
|
||||||
|
val allRanges = gatherAllQuoteRanges(text)
|
||||||
|
val bestOverall = pickBestRange(allRanges, caretOffset) ?: return null
|
||||||
|
return adjustRangeForInnerOuter(bestOverall, isOuter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adjust final range if user requested 'inner' (i.e. skip bounding chars). */
|
||||||
|
private fun adjustRangeForInnerOuter(range: TextRange, isOuter: Boolean): TextRange? {
|
||||||
|
if (isOuter) return range
|
||||||
|
// For 'inner', skip bounding chars if possible
|
||||||
|
if (range.endOffset - range.startOffset < 2) return null
|
||||||
|
return TextRange(range.startOffset + 1, range.endOffset - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather all "balanced" pairs for single/double/backtick quotes in the entire text.
|
||||||
|
* For simplicity, we treat each ["]...["] or [']...['] or [`]...[`] as one range,
|
||||||
|
* ignoring complicated cases of escaping, multi-line, etc.
|
||||||
|
*/
|
||||||
|
private fun gatherAllQuoteRanges(text: CharSequence): List<TextRange> {
|
||||||
|
val results = mutableListOf<TextRange>()
|
||||||
|
val patterns = listOf(
|
||||||
|
"\"([^\"]*)\"",
|
||||||
|
"'([^']*)'",
|
||||||
|
"`([^`]*)`"
|
||||||
|
)
|
||||||
|
for (p in patterns) {
|
||||||
|
Regex(p).findAll(text).forEach {
|
||||||
|
results.add(TextRange(it.range.first, it.range.last + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a text range for brackets using a "cover-or-next" approach.
|
||||||
|
* We treat bracket pairs ( (), [], {}, <> ) in a naive balanced scanning way.
|
||||||
|
* If [isOuter] is false, we shrink boundaries to skip the bracket chars.
|
||||||
|
*/
|
||||||
|
private fun findBracketRange(editor: VimEditor, caret: ImmutableVimCaret, isOuter: Boolean): TextRange? {
|
||||||
|
val text = editor.text()
|
||||||
|
val caretOffset = caret.offset
|
||||||
|
val caretLine = caret.getLine()
|
||||||
|
|
||||||
|
// 1) Gather bracket pairs in *this caret's line*
|
||||||
|
val lineStart = editor.getLineStartOffset(caretLine)
|
||||||
|
val lineEnd = editor.getLineEndOffset(caretLine)
|
||||||
|
val bracketChars = listOf('(', ')', '[', ']', '{', '}', '<', '>')
|
||||||
|
// Gather local line bracket pairs
|
||||||
|
val lineText = text.substring(lineStart, lineEnd)
|
||||||
|
val lineRanges = gatherAllBracketRanges(lineText, bracketChars).map {
|
||||||
|
// Shift each range's offsets to the global text
|
||||||
|
TextRange(it.startOffset + lineStart, it.endOffset + lineStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick the best match on this line
|
||||||
|
val localBest = pickBestRange(lineRanges, caretOffset)
|
||||||
|
if (localBest != null) {
|
||||||
|
return adjustRangeForInnerOuter(localBest, isOuter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Fallback: gather bracket pairs in the entire file
|
||||||
|
val allRanges = gatherAllBracketRanges(text, bracketChars)
|
||||||
|
val bestOverall = pickBestRange(allRanges, caretOffset) ?: return null
|
||||||
|
|
||||||
|
return adjustRangeForInnerOuter(bestOverall, isOuter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gathers naive "balanced bracket" ranges for the given bracket pairs.
|
||||||
|
* This is a simplified stack-based approach scanning the entire file text.
|
||||||
|
*/
|
||||||
|
private fun gatherAllBracketRanges(
|
||||||
|
text: CharSequence,
|
||||||
|
brackets: List<Char>
|
||||||
|
): List<TextRange> {
|
||||||
|
val pairs = mapOf('(' to ')', '[' to ']', '{' to '}', '<' to '>')
|
||||||
|
val results = mutableListOf<TextRange>()
|
||||||
|
val stack = ArrayDeque<Int>() // offsets of open bracket
|
||||||
|
val bracketTypeStack = ArrayDeque<Char>() // store which bracket
|
||||||
|
|
||||||
|
text.forEachIndexed { i, ch ->
|
||||||
|
if (pairs.containsKey(ch)) {
|
||||||
|
// Opening bracket
|
||||||
|
stack.addLast(i)
|
||||||
|
bracketTypeStack.addLast(ch)
|
||||||
|
} else {
|
||||||
|
// Maybe a closing bracket?
|
||||||
|
val top = bracketTypeStack.lastOrNull() ?: '\u0000'
|
||||||
|
if (pairs[top] == ch) {
|
||||||
|
// Balanced pair
|
||||||
|
val openPos = stack.removeLast()
|
||||||
|
bracketTypeStack.removeLast()
|
||||||
|
results.add(TextRange(openPos, i + 1)) // i+1 for endOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks best range among [candidates] in a “cover-or-next” approach:
|
||||||
|
* 1) Among those covering [caretOffset], pick the narrowest.
|
||||||
|
* 2) Else pick the "next" bracket whose start >= caret, if any (closest).
|
||||||
|
* 3) Else pick the "previous" bracket whose end <= caret, if any (closest).
|
||||||
|
*/
|
||||||
|
private fun pickBestRange(candidates: List<TextRange>, caretOffset: Int): TextRange? {
|
||||||
|
if (candidates.isEmpty()) return null
|
||||||
|
val covering = mutableListOf<TextRange>()
|
||||||
|
val nextOnes = mutableListOf<TextRange>()
|
||||||
|
val prevOnes = mutableListOf<TextRange>()
|
||||||
|
|
||||||
|
for (r in candidates) {
|
||||||
|
if (r.startOffset <= caretOffset && caretOffset < r.endOffset) {
|
||||||
|
covering.add(r)
|
||||||
|
} else if (r.startOffset >= caretOffset) {
|
||||||
|
nextOnes.add(r)
|
||||||
|
} else if (r.endOffset <= caretOffset) {
|
||||||
|
prevOnes.add(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Covering, smallest width
|
||||||
|
if (covering.isNotEmpty()) {
|
||||||
|
return covering.minByOrNull { it.endOffset - it.startOffset }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Next (closest by startOffset)
|
||||||
|
if (nextOnes.isNotEmpty()) {
|
||||||
|
return nextOnes.minByOrNull { kotlin.math.abs(it.startOffset - caretOffset) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Previous (closest by endOffset)
|
||||||
|
if (prevOnes.isNotEmpty()) {
|
||||||
|
return prevOnes.minByOrNull { kotlin.math.abs(it.endOffset - caretOffset) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
@ -34,7 +34,6 @@ import com.maddyhome.idea.vim.helper.SearchOptions
|
|||||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.findWordUnderCursor
|
|
||||||
import com.maddyhome.idea.vim.helper.inVisualMode
|
import com.maddyhome.idea.vim.helper.inVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
||||||
import com.maddyhome.idea.vim.helper.userData
|
import com.maddyhome.idea.vim.helper.userData
|
||||||
@ -235,7 +234,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
|||||||
val text = if (editor.inVisualMode) {
|
val text = if (editor.inVisualMode) {
|
||||||
primaryCaret.selectedText ?: return
|
primaryCaret.selectedText ?: return
|
||||||
} else {
|
} else {
|
||||||
val range = findWordUnderCursor(editor, primaryCaret) ?: return
|
val range = injector.searchHelper.findWordNearestCursor(editor.vim, primaryCaret.vim) ?: return
|
||||||
if (range.startOffset > primaryCaret.offset) return
|
if (range.startOffset > primaryCaret.offset) return
|
||||||
IjVimEditor(editor).getText(range)
|
IjVimEditor(editor).getText(range)
|
||||||
}
|
}
|
||||||
@ -300,7 +299,8 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
||||||
val range = findWordUnderCursor(editor, caret) ?: return null
|
// TODO: I think vim-multiple-cursors uses a text object rather than the star operator
|
||||||
|
val range = injector.searchHelper.findWordNearestCursor(editor.vim, caret.vim) ?: return null
|
||||||
if (range.startOffset > caret.offset) return null
|
if (range.startOffset > caret.offset) return null
|
||||||
|
|
||||||
enterVisualMode(editor.vim)
|
enterVisualMode(editor.vim)
|
||||||
|
@ -29,7 +29,6 @@ import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
|||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
|
|
||||||
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
|
||||||
@ -144,7 +143,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||||
val registerGroup = injector.registerGroup
|
val registerGroup = injector.registerGroup
|
||||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
val savedRegister = caret.registerStorage.getRegister(editor.vim, context.vim, lastRegisterChar) ?: return
|
||||||
|
|
||||||
var usedType = savedRegister.type
|
var usedType = savedRegister.type
|
||||||
var usedText = savedRegister.text
|
var usedText = savedRegister.text
|
||||||
@ -154,8 +153,7 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
|
|||||||
usedType = SelectionType.CHARACTER_WISE
|
usedType = SelectionType.CHARACTER_WISE
|
||||||
}
|
}
|
||||||
|
|
||||||
val copiedText = IjVimCopiedText(usedText, (savedRegister.copiedText as IjVimCopiedText).transferableData)
|
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
|
||||||
val textData = PutData.TextData(savedRegister.name, copiedText, usedType)
|
|
||||||
|
|
||||||
val putData = PutData(
|
val putData = PutData(
|
||||||
textData,
|
textData,
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.maddyhome.idea.vim.extension.surround
|
||||||
|
|
||||||
|
import com.intellij.util.text.CharSequenceSubSequence
|
||||||
|
|
||||||
|
internal data class RepeatedCharSequence(val text: CharSequence, val count: Int) : CharSequence {
|
||||||
|
override val length = text.length * count
|
||||||
|
|
||||||
|
override fun get(index: Int): Char {
|
||||||
|
if (index < 0 || index >= length) throw IndexOutOfBoundsException()
|
||||||
|
return text[index % text.length]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||||||
|
return CharSequenceSubSequence(this, startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return text.repeat(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun of(text: CharSequence, count: Int): CharSequence {
|
||||||
|
return when (count) {
|
||||||
|
0 -> ""
|
||||||
|
1 -> text
|
||||||
|
else -> RepeatedCharSequence(text, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
|
import com.maddyhome.idea.vim.api.VimChangeGroup
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.endsWithNewLine
|
import com.maddyhome.idea.vim.api.endsWithNewLine
|
||||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||||
@ -36,7 +37,10 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.group.findBlockRange
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
|
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
|
import com.maddyhome.idea.vim.newapi.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.options.helpers.ClipboardOptionHelper
|
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
||||||
@ -47,7 +51,6 @@ import com.maddyhome.idea.vim.state.mode.selectionType
|
|||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Port of vim-surround.
|
* Port of vim-surround.
|
||||||
@ -80,7 +83,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
private class YSurroundHandler : ExtensionHandler {
|
private class YSurroundHandler : ExtensionHandler {
|
||||||
@ -108,7 +111,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
|
val lastNonWhiteSpaceOffset = getLastNonWhitespaceCharacterOffset(editor.text(), lineStartOffset, lineEndOffset)
|
||||||
if (lastNonWhiteSpaceOffset != null) {
|
if (lastNonWhiteSpaceOffset != null) {
|
||||||
val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
|
val range = TextRange(lineStartOffset, lastNonWhiteSpaceOffset + 1)
|
||||||
performSurround(pair, range, it)
|
performSurround(pair, range, it, count = operatorArguments.count1)
|
||||||
}
|
}
|
||||||
// it.moveToOffset(lineStartOffset)
|
// it.moveToOffset(lineStartOffset)
|
||||||
}
|
}
|
||||||
@ -131,15 +134,13 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
private class VSurroundHandler : ExtensionHandler {
|
private class VSurroundHandler : ExtensionHandler {
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val selectionStart = editor.ij.caretModel.primaryCaret.selectionStart
|
|
||||||
// NB: Operator ignores SelectionType anyway
|
// NB: Operator ignores SelectionType anyway
|
||||||
if (!Operator().apply(editor, context, editor.mode.selectionType)) {
|
if (!Operator(supportsMultipleCursors = true, count = operatorArguments.count1).apply(editor, context, editor.mode.selectionType)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
// Leave visual mode
|
// Leave visual mode
|
||||||
editor.exitVisualMode()
|
editor.exitVisualMode()
|
||||||
editor.ij.caretModel.moveToOffset(selectionStart)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,10 +161,14 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
|
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
|
||||||
|
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
|
||||||
// Save old register values for carets
|
// Save old register values for carets
|
||||||
val surroundings = editor.sortedCarets()
|
val surroundings = editor.sortedCarets()
|
||||||
.map {
|
.map {
|
||||||
val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it)
|
val oldValue: List<KeyStroke>? = getRegisterForCaret(editor, context, REGISTER, it)
|
||||||
setRegisterForCaret(REGISTER, it, null)
|
setRegisterForCaret(REGISTER, it, null)
|
||||||
SurroundingInfo(it, null, oldValue, false)
|
SurroundingInfo(it, null, oldValue, false)
|
||||||
}
|
}
|
||||||
@ -177,10 +182,12 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val currentSurrounding = getCurrentSurrounding(it.caret, pick(charFrom))
|
val currentSurrounding = getCurrentSurrounding(it.caret, pick(charFrom))
|
||||||
if (currentSurrounding != null) {
|
if (currentSurrounding != null) {
|
||||||
it.caret.moveToOffset(currentSurrounding.startOffset)
|
it.caret.moveToOffset(currentSurrounding.startOffset)
|
||||||
editor.deleteString(currentSurrounding)
|
injector.application.runWriteAction {
|
||||||
|
editor.deleteString(currentSurrounding)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val registerValue = getRegisterForCaret(REGISTER, it.caret)
|
val registerValue = getRegisterForCaret(editor, context, REGISTER, it.caret)
|
||||||
val innerValue = if (registerValue.isNullOrEmpty()) emptyList() else registerValue
|
val innerValue = if (registerValue.isNullOrEmpty()) emptyList() else registerValue
|
||||||
it.innerText = innerValue
|
it.innerText = innerValue
|
||||||
|
|
||||||
@ -200,7 +207,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
val trimmedValue = if (newSurround.shouldTrim) innerValue.trim() else innerValue
|
val trimmedValue = if (newSurround.shouldTrim) innerValue.trim() else innerValue
|
||||||
it.first + trimmedValue + it.second
|
it.first + trimmedValue + it.second
|
||||||
} ?: innerValue
|
} ?: innerValue
|
||||||
val textData = PutData.TextData(null, injector.clipboardManager.dumbCopiedText(text), SelectionType.CHARACTER_WISE)
|
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
|
||||||
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
|
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
|
||||||
|
|
||||||
surrounding.caret to putData
|
surrounding.caret to putData
|
||||||
@ -271,25 +278,46 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Operator : OperatorFunction {
|
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
||||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||||
val ijEditor = editor.ij
|
val ijEditor = vimEditor.ij
|
||||||
val c = getChar(ijEditor)
|
val c = getChar(ijEditor)
|
||||||
if (c.code == 0) return true
|
if (c.code == 0) return true
|
||||||
|
|
||||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
||||||
// XXX: Will it work with line-wise or block-wise selections?
|
|
||||||
val range = getSurroundRange(editor.currentCaret()) ?: return false
|
runWriteAction {
|
||||||
performSurround(pair, range, editor.currentCaret(), selectionType == SelectionType.LINE_WISE)
|
val change = VimPlugin.getChange()
|
||||||
// Jump back to start
|
if (supportsMultipleCursors) {
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
ijEditor.runWithEveryCaretAndRestore {
|
||||||
|
applyOnce(ijEditor, change, pair, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
applyOnce(ijEditor, change, pair, count)
|
||||||
|
// Jump back to start
|
||||||
|
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: SurroundPair, count: Int) {
|
||||||
|
// XXX: Will it work with line-wise or block-wise selections?
|
||||||
|
val primaryCaret = editor.caretModel.primaryCaret
|
||||||
|
val range = getSurroundRange(primaryCaret.vim)
|
||||||
|
if (range != null) {
|
||||||
|
val start = RepeatedCharSequence.of(pair.first, count)
|
||||||
|
val end = RepeatedCharSequence.of(pair.second, count)
|
||||||
|
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.startOffset, start)
|
||||||
|
change.insertText(IjVimEditor(editor), IjVimCaret(primaryCaret), range.endOffset + start.length, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
if (editor.mode is Mode.CMD_LINE) {
|
if (editor.mode is Mode.CMD_LINE) {
|
||||||
editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
|
editor.mode = editor.mode.returnTo
|
||||||
}
|
}
|
||||||
return when (editor.mode) {
|
return when (editor.mode) {
|
||||||
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
|
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
|
||||||
@ -337,7 +365,7 @@ private fun getSurroundPair(c: Char): SurroundPair? = if (c in SURROUND_PAIRS) {
|
|||||||
private fun inputTagPair(editor: Editor, context: DataContext): SurroundPair? {
|
private fun inputTagPair(editor: Editor, context: DataContext): SurroundPair? {
|
||||||
val tagInput = inputString(editor, context, "<", '>')
|
val tagInput = inputString(editor, context, "<", '>')
|
||||||
if (editor.vim.mode is Mode.CMD_LINE) {
|
if (editor.vim.mode is Mode.CMD_LINE) {
|
||||||
editor.vim.mode = editor.vim.mode.returnTo()
|
editor.vim.mode = editor.vim.mode.returnTo
|
||||||
}
|
}
|
||||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||||
return if (matcher.find()) {
|
return if (matcher.find()) {
|
||||||
@ -356,7 +384,7 @@ private fun inputFunctionName(
|
|||||||
): SurroundPair? {
|
): SurroundPair? {
|
||||||
val functionNameInput = inputString(editor, context, "function: ", null)
|
val functionNameInput = inputString(editor, context, "function: ", null)
|
||||||
if (editor.vim.mode is Mode.CMD_LINE) {
|
if (editor.vim.mode is Mode.CMD_LINE) {
|
||||||
editor.vim.mode = editor.vim.mode.returnTo()
|
editor.vim.mode = editor.vim.mode.returnTo
|
||||||
}
|
}
|
||||||
if (functionNameInput.isEmpty()) return null
|
if (functionNameInput.isEmpty()) return null
|
||||||
return if (withInternalSpaces) {
|
return if (withInternalSpaces) {
|
||||||
@ -385,15 +413,15 @@ private fun getChar(editor: Editor): Char {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performSurround(pair: SurroundPair, range: TextRange, caret: VimCaret, tagsOnNewLines: Boolean = false) {
|
private fun performSurround(pair: SurroundPair, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
val change = VimPlugin.getChange()
|
val change = VimPlugin.getChange()
|
||||||
val leftSurround = pair.first + if (tagsOnNewLines) "\n" else ""
|
val leftSurround = RepeatedCharSequence.of(pair.first + if (tagsOnNewLines) "\n" else "", count)
|
||||||
|
|
||||||
val isEOF = range.endOffset == editor.text().length
|
val isEOF = range.endOffset == editor.text().length
|
||||||
val hasNewLine = editor.endsWithNewLine()
|
val hasNewLine = editor.endsWithNewLine()
|
||||||
val rightSurround = if (tagsOnNewLines) {
|
val rightSurround = (if (tagsOnNewLines) {
|
||||||
if (isEOF && !hasNewLine) {
|
if (isEOF && !hasNewLine) {
|
||||||
"\n" + pair.second
|
"\n" + pair.second
|
||||||
} else {
|
} else {
|
||||||
@ -401,7 +429,7 @@ private fun performSurround(pair: SurroundPair, range: TextRange, caret: VimCare
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pair.second
|
pair.second
|
||||||
}
|
}).let { RepeatedCharSequence.of(it, count) }
|
||||||
|
|
||||||
change.insertText(editor, caret, range.startOffset, leftSurround)
|
change.insertText(editor, caret, range.startOffset, leftSurround)
|
||||||
change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)
|
change.insertText(editor, caret, range.endOffset + leftSurround.length, rightSurround)
|
||||||
|
@ -14,7 +14,9 @@ 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.VimInjectorKt;
|
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.MappingMode;
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments;
|
||||||
|
import com.maddyhome.idea.vim.command.TextObjectVisualType;
|
||||||
import com.maddyhome.idea.vim.common.TextRange;
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension;
|
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||||
|
@ -14,7 +14,9 @@ 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.VimInjectorKt;
|
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.MappingMode;
|
||||||
|
import com.maddyhome.idea.vim.command.OperatorArguments;
|
||||||
|
import com.maddyhome.idea.vim.command.TextObjectVisualType;
|
||||||
import com.maddyhome.idea.vim.common.TextRange;
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
import com.maddyhome.idea.vim.extension.ExtensionHandler;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension;
|
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||||
|
@ -42,7 +42,6 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
|
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
|
||||||
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
|
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides all the insert/replace related functionality
|
* Provides all the insert/replace related functionality
|
||||||
@ -130,7 +129,9 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val project = (editor as IjVimEditor).editor.project ?: return
|
val project = (editor as IjVimEditor).editor.project ?: return
|
||||||
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
||||||
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
||||||
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
injector.application.runWriteAction {
|
||||||
|
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun autoIndentRange(
|
override fun autoIndentRange(
|
||||||
@ -139,6 +140,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
) {
|
) {
|
||||||
|
val startPos = editor.offsetToBufferPosition(caret.offset)
|
||||||
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
||||||
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
@ -163,11 +165,7 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val afterAction = {
|
val afterAction = {
|
||||||
val firstLine = editor.offsetToBufferPosition(
|
caret.moveToOffset(injector.motion.moveCaretToLineStartSkipLeading(editor, startPos.line))
|
||||||
min(startOffset.toDouble(), endOffset.toDouble()).toInt()
|
|
||||||
).line
|
|
||||||
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
|
|
||||||
caret.moveToOffset(newOffset)
|
|
||||||
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
|
restoreCursor(editor, caret, (caret as IjVimCaret).caret.logicalPosition.line)
|
||||||
}
|
}
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
@ -186,12 +184,24 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
@Deprecated(
|
||||||
|
message = "Please use listenersNotifier",
|
||||||
|
replaceWith = ReplaceWith(
|
||||||
|
"injector.listenersNotifier.modeChangeListeners.add",
|
||||||
|
imports = ["import com.maddyhome.idea.vim.api.injector"]
|
||||||
|
)
|
||||||
|
)
|
||||||
fun addInsertListener(listener: VimInsertListener) {
|
fun addInsertListener(listener: VimInsertListener) {
|
||||||
injector.listenersNotifier.modeChangeListeners.add(listener)
|
injector.listenersNotifier.modeChangeListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
@Deprecated(
|
||||||
|
message = "Please use listenersNotifier",
|
||||||
|
replaceWith = ReplaceWith(
|
||||||
|
"injector.listenersNotifier.modeChangeListeners.remove",
|
||||||
|
imports = ["import com.maddyhome.idea.vim.api.injector"]
|
||||||
|
)
|
||||||
|
)
|
||||||
fun removeInsertListener(listener: VimInsertListener) {
|
fun removeInsertListener(listener: VimInsertListener) {
|
||||||
injector.listenersNotifier.modeChangeListeners.remove(listener)
|
injector.listenersNotifier.modeChangeListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
|
||||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||||
@ -43,7 +42,7 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.intellij.openapi.editor.EditorSettings.*;
|
import static com.intellij.openapi.editor.EditorSettings.LineNumerationType;
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
||||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
||||||
@ -157,7 +156,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
|
|
||||||
private static void repaintRelativeLineNumbers(final @NotNull Editor editor) {
|
private static void repaintRelativeLineNumbers(final @NotNull Editor editor) {
|
||||||
final EditorGutter gutter = editor.getGutter();
|
final EditorGutter gutter = editor.getGutter();
|
||||||
final EditorGutterComponentEx gutterComponent = gutter instanceof EditorGutterComponentEx ? (EditorGutterComponentEx) gutter : null;
|
final EditorGutterComponentEx gutterComponent =
|
||||||
|
gutter instanceof EditorGutterComponentEx ? (EditorGutterComponentEx)gutter : null;
|
||||||
if (gutterComponent != null) {
|
if (gutterComponent != null) {
|
||||||
gutterComponent.repaint();
|
gutterComponent.repaint();
|
||||||
}
|
}
|
||||||
@ -252,18 +252,18 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyIdeaJoin(@NotNull VimEditor editor) {
|
public void notifyIdeaJoin(@NotNull VimEditor editor) {
|
||||||
notifyIdeaJoin(((IjVimEditor) editor).getEditor().getProject(), editor);
|
notifyIdeaJoin(((IjVimEditor)editor).getEditor().getProject(), editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCaretsVisualAttributes(@NotNull VimEditor editor) {
|
public void updateCaretsVisualAttributes(@NotNull VimEditor editor) {
|
||||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
Editor ijEditor = ((IjVimEditor)editor).getEditor();
|
||||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateCaretsVisualPosition(@NotNull VimEditor editor) {
|
public void updateCaretsVisualPosition(@NotNull VimEditor editor) {
|
||||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
Editor ijEditor = ((IjVimEditor)editor).getEditor();
|
||||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,26 +310,21 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Collection<VimEditor> getEditorsRaw() {
|
public @NotNull Collection<VimEditor> getEditorsRaw() {
|
||||||
return getLocalEditors()
|
return getLocalEditors().map(IjVimEditor::new).collect(Collectors.toList());
|
||||||
.map(IjVimEditor::new)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Collection<VimEditor> getEditors() {
|
public @NotNull Collection<VimEditor> getEditors() {
|
||||||
return getLocalEditors()
|
return getLocalEditors().filter(UserDataManager::getVimInitialised).map(IjVimEditor::new)
|
||||||
.filter(UserDataManager::getVimInitialised)
|
|
||||||
.map(IjVimEditor::new)
|
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||||
return getLocalEditors()
|
return getLocalEditors().filter(
|
||||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||||
.map(IjVimEditor::new)
|
.map(IjVimEditor::new).collect(Collectors.toList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<Editor> getLocalEditors() {
|
private Stream<Editor> getLocalEditors() {
|
||||||
|
@ -32,17 +32,13 @@ 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.VimFileBase
|
import com.maddyhome.idea.vim.api.VimFileBase
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
|
||||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||||
import com.maddyhome.idea.vim.helper.countWords
|
|
||||||
import com.maddyhome.idea.vim.helper.fileSize
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.execute
|
import com.maddyhome.idea.vim.newapi.execute
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -265,92 +261,6 @@ class FileGroup : VimFileBase() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun displayLocationInfo(vimEditor: VimEditor) {
|
|
||||||
val editor = (vimEditor as IjVimEditor).editor
|
|
||||||
val msg = StringBuilder()
|
|
||||||
val doc = editor.document
|
|
||||||
|
|
||||||
if (injector.vimState.mode !is VISUAL) {
|
|
||||||
val lp = editor.caretModel.logicalPosition
|
|
||||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
|
||||||
var endoff = doc.getLineEndOffset(lp.line)
|
|
||||||
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
|
|
||||||
endoff--
|
|
||||||
}
|
|
||||||
val ecol = endoff - doc.getLineStartOffset(lp.line)
|
|
||||||
val elp = editor.offsetToLogicalPosition(endoff)
|
|
||||||
|
|
||||||
msg.append("Col ").append(col + 1)
|
|
||||||
if (col != lp.column) {
|
|
||||||
msg.append("-").append(lp.column + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.append(" of ").append(ecol + 1)
|
|
||||||
if (ecol != elp.column) {
|
|
||||||
msg.append("-").append(elp.column + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
val lline = editor.caretModel.logicalPosition.line
|
|
||||||
val total = IjVimEditor(editor).lineCount()
|
|
||||||
|
|
||||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
|
|
||||||
|
|
||||||
val cp = countWords(vimEditor)
|
|
||||||
|
|
||||||
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
|
|
||||||
|
|
||||||
val offset = editor.caretModel.offset
|
|
||||||
val size = editor.fileSize
|
|
||||||
|
|
||||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
|
|
||||||
} else {
|
|
||||||
msg.append("Selected ")
|
|
||||||
|
|
||||||
val vr = TextRange(
|
|
||||||
editor.selectionModel.blockSelectionStarts,
|
|
||||||
editor.selectionModel.blockSelectionEnds
|
|
||||||
)
|
|
||||||
vr.normalize()
|
|
||||||
|
|
||||||
val lines: Int
|
|
||||||
var cp = countWords(vimEditor)
|
|
||||||
val words = cp.count
|
|
||||||
var word = 0
|
|
||||||
if (vr.isMultiple) {
|
|
||||||
lines = vr.size()
|
|
||||||
val cols = vr.maxLength
|
|
||||||
|
|
||||||
msg.append(cols).append(" Cols; ")
|
|
||||||
|
|
||||||
for (i in 0 until vr.size()) {
|
|
||||||
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
|
|
||||||
word += cp.count
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val slp = editor.offsetToLogicalPosition(vr.startOffset)
|
|
||||||
val elp = editor.offsetToLogicalPosition(vr.endOffset)
|
|
||||||
|
|
||||||
lines = elp.line - slp.line + 1
|
|
||||||
|
|
||||||
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
|
|
||||||
word = cp.count
|
|
||||||
}
|
|
||||||
|
|
||||||
val total = IjVimEditor(editor).lineCount()
|
|
||||||
|
|
||||||
msg.append(lines).append(" of ").append(total).append(" Lines")
|
|
||||||
|
|
||||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
|
|
||||||
|
|
||||||
val chars = vr.selectionCount
|
|
||||||
val size = editor.fileSize
|
|
||||||
|
|
||||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
VimPlugin.showMessage(msg.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
val msg = StringBuilder()
|
val msg = StringBuilder()
|
||||||
|
@ -14,7 +14,10 @@ import com.intellij.openapi.components.State;
|
|||||||
import com.intellij.openapi.components.Storage;
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.history.*;
|
import com.maddyhome.idea.vim.history.HistoryBlock;
|
||||||
|
import com.maddyhome.idea.vim.history.HistoryEntry;
|
||||||
|
import com.maddyhome.idea.vim.history.VimHistory;
|
||||||
|
import com.maddyhome.idea.vim.history.VimHistoryBase;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -25,8 +28,7 @@ import java.util.List;
|
|||||||
import static com.maddyhome.idea.vim.history.HistoryConstants.*;
|
import static com.maddyhome.idea.vim.history.HistoryConstants.*;
|
||||||
|
|
||||||
@State(name = "VimHistorySettings", storages = {
|
@State(name = "VimHistorySettings", storages = {
|
||||||
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
|
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)})
|
||||||
})
|
|
||||||
public class HistoryGroup extends VimHistoryBase implements PersistentStateComponent<Element> {
|
public class HistoryGroup extends VimHistoryBase implements PersistentStateComponent<Element> {
|
||||||
|
|
||||||
public void saveData(@NotNull Element element) {
|
public void saveData(@NotNull Element element) {
|
||||||
@ -105,7 +107,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
|||||||
return INPUT;
|
return INPUT;
|
||||||
}
|
}
|
||||||
if (type instanceof VimHistory.Type.Custom) {
|
if (type instanceof VimHistory.Type.Custom) {
|
||||||
return ((Type.Custom) type).getId();
|
return ((Type.Custom)type).getId();
|
||||||
}
|
}
|
||||||
return "unreachable";
|
return "unreachable";
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(sco
|
|||||||
*
|
*
|
||||||
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
||||||
*/
|
*/
|
||||||
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
|
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE) : GlobalIjOptions(scope) {
|
||||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
||||||
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
||||||
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
||||||
|
@ -35,7 +35,7 @@ object IjOptions {
|
|||||||
|
|
||||||
// We also override the default value of 'clipboard', because IntelliJ supports the 'ideaput' item, which will use
|
// We also override the default value of 'clipboard', because IntelliJ supports the 'ideaput' item, which will use
|
||||||
// IntelliJ's paste processes (e.g. to convert pasted Java to Kotlin)
|
// IntelliJ's paste processes (e.g. to convert pasted Java to Kotlin)
|
||||||
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
|
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
||||||
@ -122,6 +122,7 @@ object IjOptions {
|
|||||||
IjOptionConstants.ideavimsupportValues
|
IjOptionConstants.ideavimsupportValues
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val ideawrite: StringOption = addOption(
|
val ideawrite: StringOption = addOption(
|
||||||
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
||||||
@ -131,17 +132,21 @@ object IjOptions {
|
|||||||
"lookupkeys",
|
"lookupkeys",
|
||||||
GLOBAL,
|
GLOBAL,
|
||||||
"lookupkeys",
|
"lookupkeys",
|
||||||
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||||
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||||
|
|
||||||
// Temporary feature flags during development, not really intended for external use
|
// Temporary feature flags during development, not really intended for external use
|
||||||
val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
val closenotebooks: ToggleOption =
|
||||||
val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||||
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", false, isHidden = true))
|
val commandOrMotionAnnotation: ToggleOption =
|
||||||
|
addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||||
|
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||||
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||||
val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
val vimscriptFunctionAnnotation: ToggleOption =
|
||||||
|
addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||||
|
|
||||||
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
||||||
// derives from Option<VimInt>
|
// derives from Option<VimInt>
|
||||||
|
@ -21,7 +21,7 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class IjVimPsiService: VimPsiService {
|
class IjVimPsiService : VimPsiService {
|
||||||
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
|
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
|
||||||
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
||||||
val psiElement = psiFile.findElementAt(pos) ?: return null
|
val psiElement = psiFile.findElementAt(pos) ?: return null
|
||||||
|
@ -42,14 +42,14 @@ internal class IjVimStorageService : VimStorageServiceBase() {
|
|||||||
private val ijKeys = mutableMapOf<String, Key<out Any?>>()
|
private val ijKeys = mutableMapOf<String, Key<out Any?>>()
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private val <T> com.maddyhome.idea.vim.api.Key<T>.ij : Key<T>
|
private val <T> com.maddyhome.idea.vim.api.Key<T>.ij: Key<T>
|
||||||
get(): Key<T> {
|
get(): Key<T> {
|
||||||
val storedIjKey = ijKeys[this.name]
|
val storedIjKey = ijKeys[this.name]
|
||||||
if (storedIjKey != null) {
|
if (storedIjKey != null) {
|
||||||
return storedIjKey as Key<T>
|
return storedIjKey as Key<T>
|
||||||
|
}
|
||||||
|
val newKey = Key<T>(this.name)
|
||||||
|
ijKeys[this.name] = newKey
|
||||||
|
return newKey
|
||||||
}
|
}
|
||||||
val newKey = Key<T>(this.name)
|
|
||||||
ijKeys[this.name] = newKey
|
|
||||||
return newKey
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.List;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
@ -49,7 +49,7 @@ import static java.util.stream.Collectors.toList;
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
@State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
@State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
||||||
public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponent<Element>{
|
public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponent<Element> {
|
||||||
public static final @NonNls String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
|
public static final @NonNls String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
|
||||||
private static final @NonNls String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
|
private static final @NonNls String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
|
||||||
private static final @NonNls String OWNER_ATTRIBUTE = "owner";
|
private static final @NonNls String OWNER_ATTRIBUTE = "owner";
|
||||||
@ -303,8 +303,8 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull List<NativeAction> getActions(@NotNull VimEditor editor, @NotNull KeyStroke keyStroke) {
|
public @NotNull List<NativeAction> getActions(@NotNull VimEditor editor, @NotNull KeyStroke keyStroke) {
|
||||||
return getActions(((IjVimEditor)editor).getEditor().getComponent(), keyStroke).stream()
|
return getActions(((IjVimEditor)editor).getEditor().getComponent(), keyStroke).stream().map(IjNativeAction::new)
|
||||||
.map(IjNativeAction::new).collect(toList());
|
.collect(toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
private static @NotNull List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||||
@ -314,7 +314,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
if (c instanceof JComponent) {
|
if (c instanceof JComponent) {
|
||||||
final List<AnAction> actions = ActionUtil.getActions((JComponent)c);
|
final List<AnAction> actions = ActionUtil.getActions((JComponent)c);
|
||||||
for (AnAction action : actions) {
|
for (AnAction action : actions) {
|
||||||
if (action instanceof VimShortcutKeyAction) {
|
if (action instanceof VimShortcutKeyAction || action == VimShortcutKeyAction.getInstance()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
|
final Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
|
||||||
@ -334,7 +334,11 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||||
for (String id : keymap.getActionIds(keyStroke)) {
|
for (String id : keymap.getActionIds(keyStroke)) {
|
||||||
final AnAction action = ActionManager.getInstance().getAction(id);
|
final AnAction action = ActionManager.getInstance().getAction(id);
|
||||||
if (action != null) {
|
|
||||||
|
// EmptyAction is used to reserve a shortcut, but can't be executed. Code can ask for an action by ID and
|
||||||
|
// use its shortcut(s) to dynamically register a new action on a component in the UI hierarchy. It's not
|
||||||
|
// useful for our needs here - we'll pick up the real action when we get local actions.
|
||||||
|
if (action != null && !(action instanceof EmptyAction)) {
|
||||||
results.add(action);
|
results.add(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,21 +359,26 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull List<? extends KeyStroke> prefix, @NotNull VimEditor editor) {
|
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes,
|
||||||
|
@NotNull List<? extends KeyStroke> prefix,
|
||||||
|
@NotNull VimEditor editor) {
|
||||||
List<Pair<Set<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes, prefix);
|
List<Pair<Set<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes, prefix);
|
||||||
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
for (Pair<Set<MappingMode>, MappingInfo> row : rows) {
|
for (Pair<Set<MappingMode>, MappingInfo> row : rows) {
|
||||||
MappingInfo mappingInfo = row.getSecond();
|
MappingInfo mappingInfo = row.getSecond();
|
||||||
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 3, ' '));
|
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 3, ' '));
|
||||||
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()) + " ", 12, ' '));
|
builder.append(
|
||||||
|
StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()) + " ", 12,
|
||||||
|
' '));
|
||||||
builder.append(mappingInfo.isRecursive() ? " " : "*"); // Or `&` if script-local mappings being recursive
|
builder.append(mappingInfo.isRecursive() ? " " : "*"); // Or `&` if script-local mappings being recursive
|
||||||
builder.append(" "); // Should be `@` if it's a buffer-local mapping
|
builder.append(" "); // Should be `@` if it's a buffer-local mapping
|
||||||
builder.append(mappingInfo.getPresentableString());
|
builder.append(mappingInfo.getPresentableString());
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
VimOutputPanel outputPanel = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
|
VimOutputPanel outputPanel = injector.getOutputPanel()
|
||||||
|
.getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
|
||||||
outputPanel.addText(builder.toString(), true);
|
outputPanel.addText(builder.toString(), true);
|
||||||
outputPanel.show();
|
outputPanel.show();
|
||||||
return true;
|
return true;
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.maddyhome.idea.vim.group
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.ReferenceImporter
|
||||||
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.ReadAction
|
||||||
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.progress.Task
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
|
||||||
|
import java.util.function.BooleanSupplier
|
||||||
|
|
||||||
|
internal object MacroAutoImport {
|
||||||
|
fun run(editor: Editor, dataContext: DataContext) {
|
||||||
|
val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return
|
||||||
|
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
|
||||||
|
|
||||||
|
if (!FileDocumentManager.getInstance().requestWriting(editor.document, project)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val importers = ReferenceImporter.EP_NAME.extensionList
|
||||||
|
if (importers.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Auto import", true) {
|
||||||
|
override fun run(indicator: ProgressIndicator) {
|
||||||
|
val fixes = ReadAction.nonBlocking<List<BooleanSupplier>> {
|
||||||
|
val fixes = mutableListOf<BooleanSupplier>()
|
||||||
|
|
||||||
|
file.accept(object : PsiRecursiveElementWalkingVisitor() {
|
||||||
|
override fun visitElement(element: PsiElement) {
|
||||||
|
for (reference in element.references) {
|
||||||
|
if (reference.resolve() != null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (importer in importers) {
|
||||||
|
importer.computeAutoImportAtOffset(editor, file, element.textRange.startOffset, true)
|
||||||
|
?.let(fixes::add)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.visitElement(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return@nonBlocking fixes
|
||||||
|
}.executeSynchronously()
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeAndWait {
|
||||||
|
WriteCommandAction.writeCommandAction(project)
|
||||||
|
.withName("Auto Import")
|
||||||
|
.withGroupId("IdeaVimAutoImportAfterMacro")
|
||||||
|
.shouldRecordActionForActiveDocument(true)
|
||||||
|
.run<RuntimeException> {
|
||||||
|
fixes.forEach { it.asBoolean }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ package com.maddyhome.idea.vim.group
|
|||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionPhase
|
import com.intellij.codeInsight.completion.CompletionPhase
|
||||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
|
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
@ -22,6 +21,7 @@ import com.maddyhome.idea.vim.api.injector
|
|||||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||||
import com.maddyhome.idea.vim.macro.VimMacroBase
|
import com.maddyhome.idea.vim.macro.VimMacroBase
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to handle playback of macros
|
* Used to handle playback of macros
|
||||||
@ -40,9 +40,6 @@ internal class MacroGroup : VimMacroBase() {
|
|||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
total: Int,
|
total: Int,
|
||||||
) {
|
) {
|
||||||
// This is to make sure that we don't access potemkin progress from different threads
|
|
||||||
ApplicationManager.getApplication().assertWriteAccessAllowed()
|
|
||||||
|
|
||||||
val project = (editor as IjVimEditor).editor.project
|
val project = (editor as IjVimEditor).editor.project
|
||||||
val keyStack = getInstance().keyStack
|
val keyStack = getInstance().keyStack
|
||||||
if (!keyStack.hasStroke()) {
|
if (!keyStack.hasStroke()) {
|
||||||
@ -93,6 +90,9 @@ internal class MacroGroup : VimMacroBase() {
|
|||||||
} finally {
|
} finally {
|
||||||
keyStack.removeFirst()
|
keyStack.removeFirst()
|
||||||
}
|
}
|
||||||
|
if (!isInternalMacro) {
|
||||||
|
MacroAutoImport.run(editor.ij, context.ij)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInternalMacro) {
|
if (isInternalMacro) {
|
||||||
|
@ -19,7 +19,6 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow
|
|||||||
import com.maddyhome.idea.vim.KeyHandler
|
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.VimCaret
|
|
||||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||||
@ -48,13 +47,10 @@ import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
|||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||||
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
|
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
|
||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||||
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 com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.ReturnTo
|
|
||||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
|
||||||
import org.jetbrains.annotations.Range
|
import org.jetbrains.annotations.Range
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -64,10 +60,6 @@ import kotlin.math.min
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
internal class MotionGroup : VimMotionGroupBase() {
|
internal class MotionGroup : VimMotionGroupBase() {
|
||||||
override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
|
|
||||||
AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun moveCaretToFirstDisplayLine(
|
override fun moveCaretToFirstDisplayLine(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
caret: ImmutableVimCaret,
|
caret: ImmutableVimCaret,
|
||||||
@ -320,11 +312,13 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
vimEditor.exitVisualMode()
|
vimEditor.exitVisualMode()
|
||||||
KeyHandler.getInstance().reset(vimEditor)
|
KeyHandler.getInstance().reset(vimEditor)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Mode.CMD_LINE -> {
|
is Mode.CMD_LINE -> {
|
||||||
val commandLine = injector.commandLine.getActiveCommandLine() ?: return
|
val commandLine = injector.commandLine.getActiveCommandLine() ?: return
|
||||||
commandLine.close(refocusOwningEditor = false, resetCaret = false)
|
commandLine.close(refocusOwningEditor = false, resetCaret = false)
|
||||||
injector.outputPanel.getCurrentOutputPanel()?.close()
|
injector.outputPanel.getCurrentOutputPanel()?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,12 +326,7 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
} else {
|
} else {
|
||||||
val state = injector.vimState as VimStateMachineImpl
|
val state = injector.vimState as VimStateMachineImpl
|
||||||
if (state.mode is Mode.VISUAL) {
|
if (state.mode is Mode.VISUAL) {
|
||||||
val returnTo = state.mode.returnTo
|
state.mode = state.mode.returnTo
|
||||||
when (returnTo) {
|
|
||||||
ReturnTo.INSERT -> state.mode = Mode.INSERT
|
|
||||||
ReturnTo.REPLACE -> state.mode = Mode.REPLACE
|
|
||||||
null -> state.mode = Mode.NORMAL()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
KeyHandler.getInstance().reset(keyHandler.keyHandlerState, state.mode)
|
KeyHandler.getInstance().reset(keyHandler.keyHandlerState, state.mode)
|
||||||
|
@ -34,15 +34,12 @@ import com.intellij.openapi.ui.Messages
|
|||||||
import com.intellij.openapi.util.SystemInfo
|
import com.intellij.openapi.util.SystemInfo
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
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.handler.KeyMapIssue
|
import com.maddyhome.idea.vim.handler.KeyMapIssue
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.key.ShortcutOwner
|
import com.maddyhome.idea.vim.key.ShortcutOwner
|
||||||
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
|
import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
|
||||||
import com.maddyhome.idea.vim.options.OptionConstants
|
|
||||||
import com.maddyhome.idea.vim.statistic.ActionTracker
|
import com.maddyhome.idea.vim.statistic.ActionTracker
|
||||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable
|
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable
|
||||||
import com.maddyhome.idea.vim.vimscript.services.VimRcService
|
import com.maddyhome.idea.vim.vimscript.services.VimRcService
|
||||||
@ -62,74 +59,11 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
constructor() : this(null)
|
constructor() : this(null)
|
||||||
|
|
||||||
fun notifyAboutNewUndo() {
|
fun notifyAboutNewUndo() {}
|
||||||
val notification = Notification(
|
|
||||||
IDEAVIM_NOTIFICATION_ID,
|
|
||||||
"Undo in IdeaVim now works like in Vim",
|
|
||||||
"""
|
|
||||||
Caret movement is no longer a separate undo step, and full insert is undoable in one step.
|
|
||||||
""".trimIndent(),
|
|
||||||
NotificationType.INFORMATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
notification.addAction(object : DumbAwareAction("Share Feedback") {
|
fun notifyAboutIdeaPut() {}
|
||||||
override fun actionPerformed(p0: AnActionEvent) {
|
|
||||||
BrowserUtil.browse("https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
notification.notify(project)
|
fun notifyAboutIdeaJoin(editor: VimEditor) {}
|
||||||
}
|
|
||||||
|
|
||||||
fun notifyAboutIdeaPut() {
|
|
||||||
val notification = Notification(
|
|
||||||
IDEAVIM_NOTIFICATION_ID,
|
|
||||||
IDEAVIM_NOTIFICATION_TITLE,
|
|
||||||
"""Add <code>ideaput</code> to <code>clipboard</code> option to perform a put via the IDE<br/><b><code>set clipboard+=ideaput</code></b>""",
|
|
||||||
NotificationType.INFORMATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
notification.addAction(OpenIdeaVimRcAction(notification))
|
|
||||||
|
|
||||||
notification.addAction(
|
|
||||||
AppendToIdeaVimRcAction(
|
|
||||||
notification,
|
|
||||||
"set clipboard^=ideaput",
|
|
||||||
"ideaput",
|
|
||||||
) {
|
|
||||||
// Technically, we're supposed to prepend values to clipboard so that it's not added to the "exclude" item.
|
|
||||||
// Since we don't handle exclude, it's safe to append. But let's be clean.
|
|
||||||
injector.globalOptions().clipboard.prependValue(OptionConstants.clipboard_ideaput)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
notification.notify(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notifyAboutIdeaJoin(editor: VimEditor) {
|
|
||||||
val notification = Notification(
|
|
||||||
IDEAVIM_NOTIFICATION_ID,
|
|
||||||
IDEAVIM_NOTIFICATION_TITLE,
|
|
||||||
"""Put <b><code>set ideajoin</code></b> into your <code>~/.ideavimrc</code> to perform a join via the IDE""",
|
|
||||||
NotificationType.INFORMATION,
|
|
||||||
)
|
|
||||||
|
|
||||||
notification.addAction(OpenIdeaVimRcAction(notification))
|
|
||||||
|
|
||||||
notification.addAction(
|
|
||||||
AppendToIdeaVimRcAction(
|
|
||||||
notification,
|
|
||||||
"set ideajoin",
|
|
||||||
"ideajoin"
|
|
||||||
) {
|
|
||||||
// This is a global-local option. Setting it will always set the global value
|
|
||||||
injector.ijOptions(editor).ideajoin = true
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
notification.addAction(HelpLink(ideajoinExamplesUrl))
|
|
||||||
notification.notify(project)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun enableRepeatingMode() = Messages.showYesNoDialog(
|
fun enableRepeatingMode() = Messages.showYesNoDialog(
|
||||||
"Do you want to enable repeating keys in macOS on press and hold?\n\n" +
|
"Do you want to enable repeating keys in macOS on press and hold?\n\n" +
|
||||||
@ -215,6 +149,7 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
is KeyMapIssue.AddShortcut -> {
|
is KeyMapIssue.AddShortcut -> {
|
||||||
appendLine("- ${it.key} key is not assigned to the ${it.action} action.<br/>")
|
appendLine("- ${it.key} key is not assigned to the ${it.action} action.<br/>")
|
||||||
}
|
}
|
||||||
|
|
||||||
is KeyMapIssue.RemoveShortcut -> {
|
is KeyMapIssue.RemoveShortcut -> {
|
||||||
appendLine("- ${it.shortcut} key is incorrectly assigned to the ${it.action} action.<br/>")
|
appendLine("- ${it.shortcut} key is incorrectly assigned to the ${it.action} action.<br/>")
|
||||||
}
|
}
|
||||||
@ -296,7 +231,8 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
}
|
}
|
||||||
} + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
|
} + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
|
||||||
|
|
||||||
notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {
|
notification =
|
||||||
|
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {
|
||||||
it.whenExpired { notification = null }
|
it.whenExpired { notification = null }
|
||||||
it.addAction(StopTracking())
|
it.addAction(StopTracking())
|
||||||
|
|
||||||
@ -312,7 +248,8 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CopyActionId(val id: String?, val project: Project?) : DumbAwareAction(MessageHelper.message("action.copy.action.id.text")) {
|
class CopyActionId(val id: String?, val project: Project?) :
|
||||||
|
DumbAwareAction(MessageHelper.message("action.copy.action.id.text")) {
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
|
CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
|
@ -66,7 +66,7 @@ import java.nio.charset.Charset
|
|||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
internal interface IjVimOptionGroup: VimOptionGroup {
|
internal interface IjVimOptionGroup : VimOptionGroup {
|
||||||
/**
|
/**
|
||||||
* Return an accessor for options that only have a global value
|
* Return an accessor for options that only have a global value
|
||||||
*/
|
*/
|
||||||
@ -151,7 +151,7 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup, InternalOpt
|
|||||||
override fun <T : VimDataType> setOptionValueInternal(
|
override fun <T : VimDataType> setOptionValueInternal(
|
||||||
option: Option<T>,
|
option: Option<T>,
|
||||||
scope: OptionAccessScope,
|
scope: OptionAccessScope,
|
||||||
value: OptionValue<T>
|
value: OptionValue<T>,
|
||||||
) {
|
) {
|
||||||
super.setOptionValueInternal(option, scope, value)
|
super.setOptionValueInternal(option, scope, value)
|
||||||
}
|
}
|
||||||
@ -391,7 +391,6 @@ private abstract class GlobalLocalOptionToGlobalLocalIdeaSettingMapper<T : VimDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps the `'bomb'` local-to-buffer Vim option to the file's current byte order mark
|
* Maps the `'bomb'` local-to-buffer Vim option to the file's current byte order mark
|
||||||
*
|
*
|
||||||
@ -425,7 +424,8 @@ private class BombOptionMapper : LocalOptionValueOverride<VimInt> {
|
|||||||
// Use IntelliJ's own actions to modify the BOM. This will change the BOM stored in the virtual file, update the
|
// Use IntelliJ's own actions to modify the BOM. This will change the BOM stored in the virtual file, update the
|
||||||
// file contents and save it
|
// file contents and save it
|
||||||
val actionId = if (hasBom) "RemoveBom" else "AddBom"
|
val actionId = if (hasBom) "RemoveBom" else "AddBom"
|
||||||
val action = injector.actionExecutor.getAction(actionId) ?: throw ExException("Cannot find native action: $actionId")
|
val action =
|
||||||
|
injector.actionExecutor.getAction(actionId) ?: throw ExException("Cannot find native action: $actionId")
|
||||||
val context = injector.executionContextManager.getEditorExecutionContext(editor)
|
val context = injector.executionContextManager.getEditorExecutionContext(editor)
|
||||||
injector.actionExecutor.executeAction(editor, action, context)
|
injector.actionExecutor.executeAction(editor, action, context)
|
||||||
return true
|
return true
|
||||||
@ -463,8 +463,8 @@ private class BreakIndentOptionMapper(
|
|||||||
*
|
*
|
||||||
* TODO: This is a code style setting - how can we react to changes?
|
* TODO: This is a code style setting - how can we react to changes?
|
||||||
*/
|
*/
|
||||||
private class ColorColumnOptionValueProvider(private val colorColumnOption: StringListOption)
|
private class ColorColumnOptionValueProvider(private val colorColumnOption: StringListOption) :
|
||||||
: LocalOptionToGlobalLocalExternalSettingMapper<VimString>(colorColumnOption) {
|
LocalOptionToGlobalLocalExternalSettingMapper<VimString>(colorColumnOption) {
|
||||||
|
|
||||||
// The IntelliJ setting is in practice global, from the user's perspective
|
// The IntelliJ setting is in practice global, from the user's perspective
|
||||||
override val canUserModifyExternalLocalValue: Boolean = false
|
override val canUserModifyExternalLocalValue: Boolean = false
|
||||||
@ -510,8 +510,7 @@ private class ColorColumnOptionValueProvider(private val colorColumnOption: Stri
|
|||||||
// Given an empty string, hide the margin.
|
// Given an empty string, hide the margin.
|
||||||
if (value == VimString.EMPTY) {
|
if (value == VimString.EMPTY) {
|
||||||
editor.ij.settings.isRightMarginShown = false
|
editor.ij.settings.isRightMarginShown = false
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
editor.ij.settings.isRightMarginShown = true
|
editor.ij.settings.isRightMarginShown = true
|
||||||
|
|
||||||
val softMargins = mutableListOf<Int>()
|
val softMargins = mutableListOf<Int>()
|
||||||
@ -524,8 +523,7 @@ private class ColorColumnOptionValueProvider(private val colorColumnOption: Stri
|
|||||||
// We could perhaps add a property change listener from editor settings state?
|
// We could perhaps add a property change listener from editor settings state?
|
||||||
// (editor.ij as EditorImpl).state.addPropertyChangeListener(...)
|
// (editor.ij as EditorImpl).state.addPropertyChangeListener(...)
|
||||||
// (editor.ij.settings as SettingsImpl).getState().addPropertyChangeListener(...)
|
// (editor.ij.settings as SettingsImpl).getState().addPropertyChangeListener(...)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
it.toIntOrNull()?.let(softMargins::add)
|
it.toIntOrNull()?.let(softMargins::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,8 +557,8 @@ private class ColorColumnOptionValueProvider(private val colorColumnOption: Stri
|
|||||||
*
|
*
|
||||||
* Note that there isn't a global IntelliJ setting for this option.
|
* Note that there isn't a global IntelliJ setting for this option.
|
||||||
*/
|
*/
|
||||||
private class CursorLineOptionMapper(cursorLineOption: ToggleOption)
|
private class CursorLineOptionMapper(cursorLineOption: ToggleOption) :
|
||||||
: LocalOptionToGlobalLocalExternalSettingMapper<VimInt>(cursorLineOption) {
|
LocalOptionToGlobalLocalExternalSettingMapper<VimInt>(cursorLineOption) {
|
||||||
|
|
||||||
// The IntelliJ setting is in practice global, from the user's perspective
|
// The IntelliJ setting is in practice global, from the user's perspective
|
||||||
override val canUserModifyExternalLocalValue: Boolean = false
|
override val canUserModifyExternalLocalValue: Boolean = false
|
||||||
@ -667,7 +665,12 @@ private class FileEncodingOptionMapper : LocalOptionValueOverride<VimString> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSafeToReloadIn(virtualFile: VirtualFile, text: CharSequence, bytes: ByteArray, charset: Charset): Magic8 {
|
private fun isSafeToReloadIn(
|
||||||
|
virtualFile: VirtualFile,
|
||||||
|
text: CharSequence,
|
||||||
|
bytes: ByteArray,
|
||||||
|
charset: Charset,
|
||||||
|
): Magic8 {
|
||||||
val bom = virtualFile.bom
|
val bom = virtualFile.bom
|
||||||
if (bom != null && !CharsetToolkit.canHaveBom(charset, bom)) return Magic8.NO_WAY
|
if (bom != null && !CharsetToolkit.canHaveBom(charset, bom)) return Magic8.NO_WAY
|
||||||
|
|
||||||
@ -680,11 +683,9 @@ private class FileEncodingOptionMapper : LocalOptionValueOverride<VimString> {
|
|||||||
|
|
||||||
var bytesToSave = try {
|
var bytesToSave = try {
|
||||||
StringUtil.convertLineSeparators(loaded, separator).toByteArray(charset)
|
StringUtil.convertLineSeparators(loaded, separator).toByteArray(charset)
|
||||||
}
|
} catch (e: UnsupportedOperationException) {
|
||||||
catch (e: UnsupportedOperationException) {
|
|
||||||
return Magic8.NO_WAY
|
return Magic8.NO_WAY
|
||||||
}
|
} catch (e: NullPointerException) {
|
||||||
catch (e: NullPointerException) {
|
|
||||||
return Magic8.NO_WAY
|
return Magic8.NO_WAY
|
||||||
}
|
}
|
||||||
if (bom != null && !ArrayUtil.startsWith(bytesToSave, bom)) {
|
if (bom != null && !ArrayUtil.startsWith(bytesToSave, bom)) {
|
||||||
@ -718,12 +719,14 @@ private class FileFormatOptionMapper : LocalOptionValueOverride<VimString> {
|
|||||||
// We should have a virtual file for most scenarios, e.g., scratch files, commit message dialog, etc.
|
// We should have a virtual file for most scenarios, e.g., scratch files, commit message dialog, etc.
|
||||||
// The fallback window (TextComponentEditorImpl) does not have a virtual file
|
// The fallback window (TextComponentEditorImpl) does not have a virtual file
|
||||||
val separator = editor.ij.virtualFile?.let { LoadTextUtil.detectLineSeparator(it, false) }
|
val separator = editor.ij.virtualFile?.let { LoadTextUtil.detectLineSeparator(it, false) }
|
||||||
val value = VimString(when (separator) {
|
val value = VimString(
|
||||||
LineSeparator.LF.separatorString -> "unix"
|
when (separator) {
|
||||||
LineSeparator.CR.separatorString -> "mac"
|
LineSeparator.LF.separatorString -> "unix"
|
||||||
LineSeparator.CRLF.separatorString -> "dos"
|
LineSeparator.CR.separatorString -> "mac"
|
||||||
else -> if (injector.systemInfoService.isWindows) "dos" else "unix"
|
LineSeparator.CRLF.separatorString -> "dos"
|
||||||
})
|
else -> if (injector.systemInfoService.isWindows) "dos" else "unix"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// There is no difference between user/external/default - the file is always just one format
|
// There is no difference between user/external/default - the file is always just one format
|
||||||
return OptionValue.User(value)
|
return OptionValue.User(value)
|
||||||
@ -762,8 +765,8 @@ private class FileFormatOptionMapper : LocalOptionValueOverride<VimString> {
|
|||||||
/**
|
/**
|
||||||
* Maps the `'list'` local-to-window Vim option to the IntelliJ global-local whitespace setting
|
* Maps the `'list'` local-to-window Vim option to the IntelliJ global-local whitespace setting
|
||||||
*/
|
*/
|
||||||
private class ListOptionMapper(listOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
private class ListOptionMapper(listOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor) :
|
||||||
: LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(listOption, internalOptionValueAccessor) {
|
LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(listOption, internalOptionValueAccessor) {
|
||||||
|
|
||||||
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_IS_WHITESPACES_SHOWN
|
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_IS_WHITESPACES_SHOWN
|
||||||
|
|
||||||
@ -787,8 +790,8 @@ private class ListOptionMapper(listOption: ToggleOption, internalOptionValueAcce
|
|||||||
*
|
*
|
||||||
* Note that this must work with `'relativenumber'` to correctly handle the hybrid modes.
|
* Note that this must work with `'relativenumber'` to correctly handle the hybrid modes.
|
||||||
*/
|
*/
|
||||||
private class NumberOptionMapper(numberOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
private class NumberOptionMapper(numberOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor) :
|
||||||
: LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(numberOption, internalOptionValueAccessor) {
|
LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(numberOption, internalOptionValueAccessor) {
|
||||||
|
|
||||||
// This is a global-local setting, and can be modified by the user via _View | Active Editor | Show Line Numbers_
|
// This is a global-local setting, and can be modified by the user via _View | Active Editor | Show Line Numbers_
|
||||||
override val canUserModifyExternalLocalValue: Boolean = true
|
override val canUserModifyExternalLocalValue: Boolean = true
|
||||||
@ -808,13 +811,11 @@ private class NumberOptionMapper(numberOption: ToggleOption, internalOptionValue
|
|||||||
if (isShowingRelativeLineNumbers(editor.ij.settings.lineNumerationType)) {
|
if (isShowingRelativeLineNumbers(editor.ij.settings.lineNumerationType)) {
|
||||||
editor.ij.settings.lineNumerationType = LineNumerationType.HYBRID
|
editor.ij.settings.lineNumerationType = LineNumerationType.HYBRID
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
editor.ij.settings.isLineNumbersShown = true
|
editor.ij.settings.isLineNumbersShown = true
|
||||||
editor.ij.settings.lineNumerationType = LineNumerationType.ABSOLUTE
|
editor.ij.settings.lineNumerationType = LineNumerationType.ABSOLUTE
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Turn off 'number'. Hide lines if 'relativenumber' is not set, else switch to relative
|
// Turn off 'number'. Hide lines if 'relativenumber' is not set, else switch to relative
|
||||||
if (editor.ij.settings.isLineNumbersShown) {
|
if (editor.ij.settings.isLineNumbersShown) {
|
||||||
if (isShowingRelativeLineNumbers(editor.ij.settings.lineNumerationType)) {
|
if (isShowingRelativeLineNumbers(editor.ij.settings.lineNumerationType)) {
|
||||||
@ -828,7 +829,8 @@ private class NumberOptionMapper(numberOption: ToggleOption, internalOptionValue
|
|||||||
|
|
||||||
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
||||||
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_ARE_LINE_NUMBERS_SHOWN
|
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_ARE_LINE_NUMBERS_SHOWN
|
||||||
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_LINE_NUMERATION) {
|
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_LINE_NUMERATION
|
||||||
|
) {
|
||||||
doOnGlobalIdeaValueChanged()
|
doOnGlobalIdeaValueChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -863,13 +865,11 @@ private class RelativeNumberOptionMapper(
|
|||||||
if (isShowingAbsoluteLineNumbers(editor.ij.settings.lineNumerationType)) {
|
if (isShowingAbsoluteLineNumbers(editor.ij.settings.lineNumerationType)) {
|
||||||
editor.ij.settings.lineNumerationType = LineNumerationType.HYBRID
|
editor.ij.settings.lineNumerationType = LineNumerationType.HYBRID
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
editor.ij.settings.isLineNumbersShown = true
|
editor.ij.settings.isLineNumbersShown = true
|
||||||
editor.ij.settings.lineNumerationType = LineNumerationType.RELATIVE
|
editor.ij.settings.lineNumerationType = LineNumerationType.RELATIVE
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Turn off 'relativenumber'. Hide lines if 'number' is not set, else switch to relative
|
// Turn off 'relativenumber'. Hide lines if 'number' is not set, else switch to relative
|
||||||
if (editor.ij.settings.isLineNumbersShown) {
|
if (editor.ij.settings.isLineNumbersShown) {
|
||||||
if (isShowingAbsoluteLineNumbers(editor.ij.settings.lineNumerationType)) {
|
if (isShowingAbsoluteLineNumbers(editor.ij.settings.lineNumerationType)) {
|
||||||
@ -883,7 +883,8 @@ private class RelativeNumberOptionMapper(
|
|||||||
|
|
||||||
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
||||||
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_ARE_LINE_NUMBERS_SHOWN
|
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_ARE_LINE_NUMBERS_SHOWN
|
||||||
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_LINE_NUMERATION) {
|
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_LINE_NUMERATION
|
||||||
|
) {
|
||||||
doOnGlobalIdeaValueChanged()
|
doOnGlobalIdeaValueChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -914,8 +915,8 @@ private fun isShowingRelativeLineNumbers(lineNumerationType: LineNumerationType)
|
|||||||
* We can also clear the overridden IDE setting value by setting it to `-1`. So when the user resets the Vim option to
|
* We can also clear the overridden IDE setting value by setting it to `-1`. So when the user resets the Vim option to
|
||||||
* defaults, it will again map to the global IDE value. It's a shame not all IDE settings do this.
|
* defaults, it will again map to the global IDE value. It's a shame not all IDE settings do this.
|
||||||
*/
|
*/
|
||||||
private class ScrollJumpOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
private class ScrollJumpOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor) :
|
||||||
: GlobalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
|
GlobalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
|
||||||
|
|
||||||
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_VERTICAL_SCROLL_JUMP
|
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_VERTICAL_SCROLL_JUMP
|
||||||
|
|
||||||
@ -949,8 +950,8 @@ private class ScrollJumpOptionMapper(option: NumberOption, internalOptionValueAc
|
|||||||
* We can also clear the overridden IDE setting value by setting it to `-1`. So when the user resets the Vim option to
|
* We can also clear the overridden IDE setting value by setting it to `-1`. So when the user resets the Vim option to
|
||||||
* defaults, it will again map to the global IDE value. It's a shame not all IDE settings do this.
|
* defaults, it will again map to the global IDE value. It's a shame not all IDE settings do this.
|
||||||
*/
|
*/
|
||||||
private class SideScrollOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
private class SideScrollOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor) :
|
||||||
: GlobalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
|
GlobalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
|
||||||
|
|
||||||
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_HORIZONTAL_SCROLL_JUMP
|
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_HORIZONTAL_SCROLL_JUMP
|
||||||
|
|
||||||
@ -1140,8 +1141,8 @@ private abstract class OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<T :
|
|||||||
* (window) overrides. The [LocalOptionToGlobalLocalExternalSettingMapper] base class will handle this by calling
|
* (window) overrides. The [LocalOptionToGlobalLocalExternalSettingMapper] base class will handle this by calling
|
||||||
* [setLocalExternalValue] for all open editors for the changed buffer.
|
* [setLocalExternalValue] for all open editors for the changed buffer.
|
||||||
*/
|
*/
|
||||||
private class TextWidthOptionMapper(textWidthOption: NumberOption)
|
private class TextWidthOptionMapper(textWidthOption: NumberOption) :
|
||||||
: LocalOptionToGlobalLocalExternalSettingMapper<VimInt>(textWidthOption) {
|
LocalOptionToGlobalLocalExternalSettingMapper<VimInt>(textWidthOption) {
|
||||||
|
|
||||||
// The IntelliJ setting is in practice global, from the user's perspective
|
// The IntelliJ setting is in practice global, from the user's perspective
|
||||||
override val canUserModifyExternalLocalValue: Boolean = false
|
override val canUserModifyExternalLocalValue: Boolean = false
|
||||||
@ -1168,8 +1169,7 @@ private class TextWidthOptionMapper(textWidthOption: NumberOption)
|
|||||||
val project = ijEditor.project ?: ProjectManager.getInstance().defaultProject
|
val project = ijEditor.project ?: ProjectManager.getInstance().defaultProject
|
||||||
return if (ijEditor.settings.isWrapWhenTypingReachesRightMargin(project)) {
|
return if (ijEditor.settings.isWrapWhenTypingReachesRightMargin(project)) {
|
||||||
ijEditor.settings.getRightMargin(ijEditor.project).asVimInt()
|
ijEditor.settings.getRightMargin(ijEditor.project).asVimInt()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
VimInt.ZERO
|
VimInt.ZERO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1215,8 +1215,8 @@ private class TextWidthOptionMapper(textWidthOption: NumberOption)
|
|||||||
/**
|
/**
|
||||||
* Maps the `'wrap'` Vim option to the IntelliJ soft wrap settings
|
* Maps the `'wrap'` Vim option to the IntelliJ soft wrap settings
|
||||||
*/
|
*/
|
||||||
private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAccessor: InternalOptionValueAccessor) :
|
||||||
: LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(wrapOption, internalOptionValueAccessor) {
|
LocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(wrapOption, internalOptionValueAccessor) {
|
||||||
|
|
||||||
// This is a global-local setting, and can be modified by the user via _View | Active Editor | Soft-Wrap_
|
// This is a global-local setting, and can be modified by the user via _View | Active Editor | Soft-Wrap_
|
||||||
override val canUserModifyExternalLocalValue: Boolean = true
|
override val canUserModifyExternalLocalValue: Boolean = true
|
||||||
@ -1245,7 +1245,9 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
|
|||||||
fun editorKindToSoftWrapAppliancesPlace(kind: EditorKind) = when (kind) {
|
fun editorKindToSoftWrapAppliancesPlace(kind: EditorKind) = when (kind) {
|
||||||
EditorKind.UNTYPED,
|
EditorKind.UNTYPED,
|
||||||
EditorKind.DIFF,
|
EditorKind.DIFF,
|
||||||
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
EditorKind.MAIN_EDITOR,
|
||||||
|
-> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||||
|
|
||||||
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
||||||
// Treat PREVIEW as a kind of MAIN_EDITOR instead of SWAP.PREVIEW. There are fewer noticeable differences
|
// Treat PREVIEW as a kind of MAIN_EDITOR instead of SWAP.PREVIEW. There are fewer noticeable differences
|
||||||
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||||
@ -1261,7 +1263,9 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
|
|||||||
val softWrapAppliancePlace = when (editor.ij.editorKind) {
|
val softWrapAppliancePlace = when (editor.ij.editorKind) {
|
||||||
EditorKind.UNTYPED,
|
EditorKind.UNTYPED,
|
||||||
EditorKind.DIFF,
|
EditorKind.DIFF,
|
||||||
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
EditorKind.MAIN_EDITOR,
|
||||||
|
-> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||||
|
|
||||||
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
||||||
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.PREVIEW
|
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.PREVIEW
|
||||||
}
|
}
|
||||||
@ -1304,7 +1308,8 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
|
|||||||
|
|
||||||
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
||||||
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_USE_SOFT_WRAPS
|
if (propertyName == EditorSettingsExternalizable.PropNames.PROP_USE_SOFT_WRAPS
|
||||||
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_SOFT_WRAP_FILE_MASKS) {
|
|| propertyName == EditorSettingsExternalizable.PropNames.PROP_SOFT_WRAP_FILE_MASKS
|
||||||
|
) {
|
||||||
doOnGlobalIdeaValueChanged()
|
doOnGlobalIdeaValueChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1331,8 +1336,10 @@ class IjOptionConstants {
|
|||||||
const val ideawrite_file: String = "file"
|
const val ideawrite_file: String = "file"
|
||||||
|
|
||||||
val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
||||||
val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
val ideaRefactorModeValues: Set<String> =
|
||||||
|
setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
||||||
val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
|
val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
|
||||||
val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
val ideavimsupportValues: Set<String> =
|
||||||
|
setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class ProcessGroup : VimProcessGroupBase() {
|
|||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
command: String,
|
command: String,
|
||||||
input: CharSequence?,
|
input: CharSequence?,
|
||||||
currentDirectoryPath: String?
|
currentDirectoryPath: String?,
|
||||||
): String? {
|
): String? {
|
||||||
// This is a much simplified version of how Vim does this. We're using stdin/stdout directly, while Vim will
|
// This is a much simplified version of how Vim does this. We're using stdin/stdout directly, while Vim will
|
||||||
// redirect to temp files ('shellredir' and 'shelltemp') or use pipes. We don't support 'shellquote', because we're
|
// redirect to temp files ('shellredir' and 'shelltemp') or use pipes. We don't support 'shellquote', because we're
|
||||||
|
@ -14,7 +14,6 @@ import com.intellij.openapi.components.State;
|
|||||||
import com.intellij.openapi.components.Storage;
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
||||||
import com.maddyhome.idea.vim.register.Register;
|
import com.maddyhome.idea.vim.register.Register;
|
||||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
||||||
@ -29,14 +28,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This group works with command associated with copying and pasting text
|
* This group works with command associated with copying and pasting text
|
||||||
*/
|
*/
|
||||||
@State(name = "VimRegisterSettings", storages = {
|
@State(name = "VimRegisterSettings", storages = {
|
||||||
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
|
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)})
|
||||||
})
|
|
||||||
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
|
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -131,7 +127,7 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
|
|||||||
final String text = VimPlugin.getXML().getSafeXmlText(textElement);
|
final String text = VimPlugin.getXML().getSafeXmlText(textElement);
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
logger.trace("Register data parsed");
|
logger.trace("Register data parsed");
|
||||||
register = new Register(key, injector.getClipboardManager().dumbCopiedText(text), type);
|
register = new Register(key, type, text, Collections.emptyList());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.trace("Cannot parse register data");
|
logger.trace("Cannot parse register data");
|
||||||
@ -148,9 +144,8 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
|
|||||||
final int modifiers = Integer.parseInt(keyElement.getAttributeValue("mods"));
|
final int modifiers = Integer.parseInt(keyElement.getAttributeValue("mods"));
|
||||||
final char c = (char)Integer.parseInt(keyElement.getAttributeValue("char"));
|
final char c = (char)Integer.parseInt(keyElement.getAttributeValue("char"));
|
||||||
//noinspection MagicConstant
|
//noinspection MagicConstant
|
||||||
strokes.add(c == KeyEvent.CHAR_UNDEFINED ?
|
strokes.add(
|
||||||
KeyStroke.getKeyStroke(code, modifiers) :
|
c == KeyEvent.CHAR_UNDEFINED ? KeyStroke.getKeyStroke(code, modifiers) : KeyStroke.getKeyStroke(c));
|
||||||
KeyStroke.getKeyStroke(c));
|
|
||||||
}
|
}
|
||||||
register = new Register(key, type, strokes);
|
register = new Register(key, type, strokes);
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,15 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
import com.maddyhome.idea.vim.newapi.initInjector
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
import org.jdom.Element
|
import org.jdom.Element
|
||||||
|
|
||||||
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
|
@State(
|
||||||
|
name = "VimJumpsSettings",
|
||||||
|
storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)]
|
||||||
|
)
|
||||||
internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateComponent<Element?> {
|
internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateComponent<Element?> {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = vimLogger<VimJumpServiceImpl>()
|
private val logger = vimLogger<VimJumpServiceImpl>()
|
||||||
}
|
}
|
||||||
|
|
||||||
override var lastJumpTimeStamp: Long = 0
|
override var lastJumpTimeStamp: Long = 0
|
||||||
|
|
||||||
override fun includeCurrentCommandAsNavigation(editor: VimEditor) {
|
override fun includeCurrentCommandAsNavigation(editor: VimEditor) {
|
||||||
@ -93,7 +97,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
|||||||
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
|
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
|
||||||
initInjector()
|
initInjector()
|
||||||
if (!injector.globalIjOptions().unifyjumps) return
|
if (!injector.globalIjOptions().unifyjumps) return
|
||||||
|
|
||||||
val jumpService = injector.jumpService
|
val jumpService = injector.jumpService
|
||||||
if (!isChanged) {
|
if (!isChanged) {
|
||||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||||
@ -105,7 +109,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
|||||||
|
|
||||||
override fun recentPlaceRemoved(changePlace: PlaceInfo, isChanged: Boolean) {
|
override fun recentPlaceRemoved(changePlace: PlaceInfo, isChanged: Boolean) {
|
||||||
if (!injector.globalIjOptions().unifyjumps) return
|
if (!injector.globalIjOptions().unifyjumps) return
|
||||||
|
|
||||||
val jumpService = injector.jumpService
|
val jumpService = injector.jumpService
|
||||||
if (!isChanged) {
|
if (!isChanged) {
|
||||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||||
|
@ -45,7 +45,10 @@ import java.util.*
|
|||||||
// todo sync vim jumps with ide jumps
|
// todo sync vim jumps with ide jumps
|
||||||
|
|
||||||
// todo exception after moving to global mark after deleting it via IDE (impossible to receive markChar)
|
// todo exception after moving to global mark after deleting it via IDE (impossible to receive markChar)
|
||||||
@State(name = "VimMarksSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
|
@State(
|
||||||
|
name = "VimMarksSettings",
|
||||||
|
storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)]
|
||||||
|
)
|
||||||
internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateComponent<Element?> {
|
internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateComponent<Element?> {
|
||||||
private fun createOrGetSystemMark(ch: Char, line: Int, col: Int, editor: VimEditor): Mark? {
|
private fun createOrGetSystemMark(ch: Char, line: Int, col: Int, editor: VimEditor): Mark? {
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
@ -71,7 +74,8 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
|||||||
}
|
}
|
||||||
element.addContent(globalMarksElement)
|
element.addContent(globalMarksElement)
|
||||||
val localMarksElement = Element("localmarks")
|
val localMarksElement = Element("localmarks")
|
||||||
var files: List<LocalMarks<Char, Mark>> = filepathToLocalMarks.values.sortedWith(Comparator.comparing(LocalMarks<Char, Mark>::myTimestamp))
|
var files: List<LocalMarks<Char, Mark>> =
|
||||||
|
filepathToLocalMarks.values.sortedWith(Comparator.comparing(LocalMarks<Char, Mark>::myTimestamp))
|
||||||
if (files.size > SAVE_MARK_COUNT) {
|
if (files.size > SAVE_MARK_COUNT) {
|
||||||
files = files.subList(files.size - SAVE_MARK_COUNT, files.size)
|
files = files.subList(files.size - SAVE_MARK_COUNT, files.size)
|
||||||
}
|
}
|
||||||
@ -85,7 +89,12 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
|||||||
fileMarkElem.setAttribute("name", file)
|
fileMarkElem.setAttribute("name", file)
|
||||||
fileMarkElem.setAttribute("timestamp", java.lang.Long.toString(marks.myTimestamp.time))
|
fileMarkElem.setAttribute("timestamp", java.lang.Long.toString(marks.myTimestamp.time))
|
||||||
for (mark in marks.values) {
|
for (mark in marks.values) {
|
||||||
if (!Character.isUpperCase(mark.key) && injector.markService.isValidMark(mark.key, VimMarkService.Operation.SAVE, true)) {
|
if (!Character.isUpperCase(mark.key) && injector.markService.isValidMark(
|
||||||
|
mark.key,
|
||||||
|
VimMarkService.Operation.SAVE,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
) {
|
||||||
val markElem = Element("mark")
|
val markElem = Element("mark")
|
||||||
markElem.setAttribute("key", mark.key.toString())
|
markElem.setAttribute("key", mark.key.toString())
|
||||||
markElem.setAttribute("line", mark.line.toString())
|
markElem.setAttribute("line", mark.line.toString())
|
||||||
|
@ -31,8 +31,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class WindowGroup extends WindowGroupBase {
|
public class WindowGroup extends WindowGroupBase {
|
||||||
@Override
|
@Override
|
||||||
@ -56,7 +56,7 @@ public class WindowGroup extends WindowGroupBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void closeAll(@NotNull ExecutionContext context) {
|
public void closeAll(@NotNull ExecutionContext context) {
|
||||||
getFileEditorManager(((IjEditorExecutionContext) context).getContext()).closeAllFiles();
|
getFileEditorManager(((IjEditorExecutionContext)context).getContext()).closeAllFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,10 +105,13 @@ public class WindowGroup extends WindowGroupBase {
|
|||||||
@Override
|
@Override
|
||||||
@RWLockLabel.Readonly
|
@RWLockLabel.Readonly
|
||||||
@RequiresReadLock
|
@RequiresReadLock
|
||||||
public void selectWindowInRow(@NotNull VimCaret caret, @NotNull ExecutionContext context, int relativePosition, boolean vertical) {
|
public void selectWindowInRow(@NotNull VimCaret caret,
|
||||||
final Caret ijCaret = ((IjVimCaret) caret).getCaret();
|
@NotNull ExecutionContext context,
|
||||||
|
int relativePosition,
|
||||||
|
boolean vertical) {
|
||||||
|
final Caret ijCaret = ((IjVimCaret)caret).getCaret();
|
||||||
final FileEditorManagerEx fileEditorManager = getFileEditorManager(((DataContext)context.getContext()));
|
final FileEditorManagerEx fileEditorManager = getFileEditorManager(((DataContext)context.getContext()));
|
||||||
final EditorWindow currentWindow = fileEditorManager.getCurrentWindow();
|
final EditorWindow currentWindow = fileEditorManager.getCurrentWindow();
|
||||||
if (currentWindow != null) {
|
if (currentWindow != null) {
|
||||||
final EditorWindow[] windows = fileEditorManager.getWindows();
|
final EditorWindow[] windows = fileEditorManager.getWindows();
|
||||||
final List<EditorWindow> row = findWindowsInRow(ijCaret, currentWindow, Arrays.asList(windows), vertical);
|
final List<EditorWindow> row = findWindowsInRow(ijCaret, currentWindow, Arrays.asList(windows), vertical);
|
||||||
@ -116,7 +119,8 @@ public class WindowGroup extends WindowGroupBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectWindow(@NotNull EditorWindow currentWindow, @NotNull List<EditorWindow> windows,
|
private void selectWindow(@NotNull EditorWindow currentWindow,
|
||||||
|
@NotNull List<EditorWindow> windows,
|
||||||
int relativePosition) {
|
int relativePosition) {
|
||||||
final int pos = windows.indexOf(currentWindow);
|
final int pos = windows.indexOf(currentWindow);
|
||||||
final int selected = pos + relativePosition;
|
final int selected = pos + relativePosition;
|
||||||
@ -126,7 +130,8 @@ public class WindowGroup extends WindowGroupBase {
|
|||||||
|
|
||||||
private static @NotNull List<EditorWindow> findWindowsInRow(@NotNull Caret caret,
|
private static @NotNull List<EditorWindow> findWindowsInRow(@NotNull Caret caret,
|
||||||
@NotNull EditorWindow editorWindow,
|
@NotNull EditorWindow editorWindow,
|
||||||
@NotNull List<EditorWindow> windows, final boolean vertical) {
|
@NotNull List<EditorWindow> windows,
|
||||||
|
final boolean vertical) {
|
||||||
final Point anchorPoint = getCaretPoint(caret);
|
final Point anchorPoint = getCaretPoint(caret);
|
||||||
if (anchorPoint != null) {
|
if (anchorPoint != null) {
|
||||||
final List<EditorWindow> result = new ArrayList<>();
|
final List<EditorWindow> result = new ArrayList<>();
|
||||||
|
@ -19,7 +19,6 @@ import com.intellij.openapi.editor.Caret
|
|||||||
import com.intellij.openapi.editor.RangeMarker
|
import com.intellij.openapi.editor.RangeMarker
|
||||||
import com.intellij.openapi.editor.ex.EditorEx
|
import com.intellij.openapi.editor.ex.EditorEx
|
||||||
import com.intellij.openapi.ide.CopyPasteManager
|
import com.intellij.openapi.ide.CopyPasteManager
|
||||||
import com.intellij.util.PlatformUtils
|
|
||||||
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.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
@ -34,9 +33,9 @@ import com.maddyhome.idea.vim.helper.EditorHelper
|
|||||||
import com.maddyhome.idea.vim.helper.RWLockLabel
|
import com.maddyhome.idea.vim.helper.RWLockLabel
|
||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||||
import com.maddyhome.idea.vim.ide.isClionNova
|
import com.maddyhome.idea.vim.ide.isClionNova
|
||||||
|
import com.maddyhome.idea.vim.ide.isRider
|
||||||
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
|
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
|
|
||||||
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
|
||||||
@ -80,7 +79,7 @@ internal class PutGroup : VimPutBase() {
|
|||||||
vimEditor: VimEditor,
|
vimEditor: VimEditor,
|
||||||
vimContext: ExecutionContext,
|
vimContext: ExecutionContext,
|
||||||
text: ProcessedTextData,
|
text: ProcessedTextData,
|
||||||
subMode: SelectionType,
|
selectionType: SelectionType,
|
||||||
data: PutData,
|
data: PutData,
|
||||||
additionalData: Map<String, Any>,
|
additionalData: Map<String, Any>,
|
||||||
) {
|
) {
|
||||||
@ -127,7 +126,7 @@ internal class PutGroup : VimPutBase() {
|
|||||||
point.dispose()
|
point.dispose()
|
||||||
if (!caret.isValid) return@forEach
|
if (!caret.isValid) return@forEach
|
||||||
|
|
||||||
val caretPossibleEndOffset = lastPastedRegion?.endOffset ?: (startOffset + text.copiedText.text.length)
|
val caretPossibleEndOffset = lastPastedRegion?.endOffset ?: (startOffset + text.text.length)
|
||||||
val endOffset = if (data.indent) {
|
val endOffset = if (data.indent) {
|
||||||
doIndent(
|
doIndent(
|
||||||
vimEditor,
|
vimEditor,
|
||||||
@ -148,7 +147,7 @@ internal class PutGroup : VimPutBase() {
|
|||||||
startOffset,
|
startOffset,
|
||||||
endOffset,
|
endOffset,
|
||||||
text.typeInRegister,
|
text.typeInRegister,
|
||||||
subMode,
|
selectionType,
|
||||||
data.caretAfterInsertedText,
|
data.caretAfterInsertedText,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -179,9 +178,12 @@ internal class PutGroup : VimPutBase() {
|
|||||||
val allContentsBefore = CopyPasteManager.getInstance().allContents
|
val allContentsBefore = CopyPasteManager.getInstance().allContents
|
||||||
val sizeBeforeInsert = allContentsBefore.size
|
val sizeBeforeInsert = allContentsBefore.size
|
||||||
val firstItemBefore = allContentsBefore.firstOrNull()
|
val firstItemBefore = allContentsBefore.firstOrNull()
|
||||||
logger.debug { "Copied text: ${text.copiedText}" }
|
logger.debug { "Transferable classes: ${text.transferableData.joinToString { it.javaClass.name }}" }
|
||||||
val (textContent, transferableData) = text.copiedText as IjVimCopiedText
|
val origContent: TextBlockTransferable =
|
||||||
val origContent: TextBlockTransferable = injector.clipboardManager.setClipboardText(textContent, textContent, transferableData) as TextBlockTransferable
|
injector.clipboardManager.setClipboardText(
|
||||||
|
text.text,
|
||||||
|
transferableData = text.transferableData,
|
||||||
|
) as TextBlockTransferable
|
||||||
val allContentsAfter = CopyPasteManager.getInstance().allContents
|
val allContentsAfter = CopyPasteManager.getInstance().allContents
|
||||||
val sizeAfterInsert = allContentsAfter.size
|
val sizeAfterInsert = allContentsAfter.size
|
||||||
try {
|
try {
|
||||||
@ -189,7 +191,7 @@ internal class PutGroup : VimPutBase() {
|
|||||||
} finally {
|
} finally {
|
||||||
val textInClipboard = (firstItemBefore as? TextBlockTransferable)
|
val textInClipboard = (firstItemBefore as? TextBlockTransferable)
|
||||||
?.getTransferData(DataFlavor.stringFlavor) as? String
|
?.getTransferData(DataFlavor.stringFlavor) as? String
|
||||||
val textOnTop = textInClipboard != null && textInClipboard != text.copiedText.text
|
val textOnTop = textInClipboard != null && textInClipboard != text.text
|
||||||
if (sizeBeforeInsert != sizeAfterInsert || textOnTop) {
|
if (sizeBeforeInsert != sizeAfterInsert || textOnTop) {
|
||||||
// Sometimes an inserted text replaces an existing one. E.g. on insert with + or * register
|
// Sometimes an inserted text replaces an existing one. E.g. on insert with + or * register
|
||||||
(CopyPasteManager.getInstance() as? CopyPasteManagerEx)?.run { removeContent(origContent) }
|
(CopyPasteManager.getInstance() as? CopyPasteManagerEx)?.run { removeContent(origContent) }
|
||||||
@ -205,7 +207,7 @@ internal class PutGroup : VimPutBase() {
|
|||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
): Int {
|
): Int {
|
||||||
// Temp fix for VIM-2808. Should be removed after rider will fix it's issues
|
// Temp fix for VIM-2808. Should be removed after rider will fix it's issues
|
||||||
if (PlatformUtils.isRider() || isClionNova()) return endOffset
|
if (isRider() || isClionNova()) return endOffset
|
||||||
|
|
||||||
val startLine = editor.offsetToBufferPosition(startOffset).line
|
val startLine = editor.offsetToBufferPosition(startOffset).line
|
||||||
val endLine = editor.offsetToBufferPosition(endOffset - 1).line
|
val endLine = editor.offsetToBufferPosition(endOffset - 1).line
|
||||||
|
@ -12,12 +12,14 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.diagnostic.trace
|
import com.intellij.openapi.diagnostic.trace
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.util.concurrency.annotations.RequiresReadLock
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.options
|
import com.maddyhome.idea.vim.api.options
|
||||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.controlNonVimSelectionChange
|
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.controlNonVimSelectionChange
|
||||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.predictMode
|
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.predictMode
|
||||||
|
import com.maddyhome.idea.vim.helper.RWLockLabel
|
||||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
||||||
@ -33,7 +35,6 @@ import com.maddyhome.idea.vim.state.mode.inCommandLineMode
|
|||||||
import com.maddyhome.idea.vim.state.mode.inNormalMode
|
import com.maddyhome.idea.vim.state.mode.inNormalMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.IdeaRefactorModeHelper
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeKeep
|
||||||
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
|
import com.maddyhome.idea.vim.vimscript.model.options.helpers.isIdeaRefactorModeSelect
|
||||||
@ -75,7 +76,7 @@ internal object IdeaSelectionControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasSelection) {
|
if (hasSelection) {
|
||||||
if (editor.vim.inCommandLineMode && editor.vim.mode.returnTo().hasVisualSelection) {
|
if (editor.vim.inCommandLineMode && editor.vim.mode.returnTo.hasVisualSelection) {
|
||||||
logger.trace { "Modifying selection while in Command-line mode, most likely incsearch" }
|
logger.trace { "Modifying selection while in Command-line mode, most likely incsearch" }
|
||||||
return@singleTask
|
return@singleTask
|
||||||
}
|
}
|
||||||
@ -90,7 +91,8 @@ internal object IdeaSelectionControl {
|
|||||||
|
|
||||||
editor.vim.mode = Mode.NORMAL()
|
editor.vim.mode = Mode.NORMAL()
|
||||||
|
|
||||||
activateMode(editor, chooseSelectionMode(editor, selectionSource, true))
|
val mode = injector.application.runReadAction { chooseSelectionMode(editor, selectionSource, true) }
|
||||||
|
activateMode(editor, mode)
|
||||||
} else {
|
} else {
|
||||||
logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
|
logger.debug("None of carets have selection. State before adjustment: ${editor.vim.mode}")
|
||||||
if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
|
if (editor.vim.inVisualMode) editor.vim.exitVisualMode()
|
||||||
@ -116,6 +118,8 @@ internal object IdeaSelectionControl {
|
|||||||
* This method is created to improve user experience. It allows avoiding delay in some operations
|
* This method is created to improve user experience. It allows avoiding delay in some operations
|
||||||
* (because [controlNonVimSelectionChange] is not executed immediately)
|
* (because [controlNonVimSelectionChange] is not executed immediately)
|
||||||
*/
|
*/
|
||||||
|
@RWLockLabel.Readonly
|
||||||
|
@RequiresReadLock
|
||||||
fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): Mode {
|
fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): Mode {
|
||||||
if (editor.selectionModel.hasSelection(true)) {
|
if (editor.selectionModel.hasSelection(true)) {
|
||||||
if (dontChangeMode(editor)) return editor.vim.mode
|
if (dontChangeMode(editor)) return editor.vim.mode
|
||||||
@ -149,6 +153,8 @@ internal object IdeaSelectionControl {
|
|||||||
return Mode.NORMAL()
|
return Mode.NORMAL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RWLockLabel.Readonly
|
||||||
|
@RequiresReadLock
|
||||||
private fun chooseSelectionMode(
|
private fun chooseSelectionMode(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
selectionSource: VimListenerManager.SelectionSource,
|
selectionSource: VimListenerManager.SelectionSource,
|
||||||
@ -158,23 +164,27 @@ internal object IdeaSelectionControl {
|
|||||||
return when {
|
return when {
|
||||||
editor.isOneLineMode -> {
|
editor.isOneLineMode -> {
|
||||||
if (logReason) logger.debug("Enter select mode. Reason: one line mode")
|
if (logReason) logger.debug("Enter select mode. Reason: one line mode")
|
||||||
Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
|
Mode.SELECT(VimPlugin.getVisualMotion().detectSelectionType(editor.vim))
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionSource == VimListenerManager.SelectionSource.MOUSE && OptionConstants.selectmode_mouse in selectmode -> {
|
selectionSource == VimListenerManager.SelectionSource.MOUSE && OptionConstants.selectmode_mouse in selectmode -> {
|
||||||
if (logReason) logger.debug("Enter select mode. Selection source is mouse and selectMode option has mouse")
|
if (logReason) logger.debug("Enter select mode. Selection source is mouse and selectMode option has mouse")
|
||||||
Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
|
Mode.SELECT(VimPlugin.getVisualMotion().detectSelectionType(editor.vim))
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.isTemplateActive() && editor.vim.isIdeaRefactorModeSelect -> {
|
editor.isTemplateActive() && editor.vim.isIdeaRefactorModeSelect -> {
|
||||||
if (logReason) logger.debug("Enter select mode. Template is active and selectMode has template")
|
if (logReason) logger.debug("Enter select mode. Template is active and selectMode has template")
|
||||||
Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
|
Mode.SELECT(VimPlugin.getVisualMotion().detectSelectionType(editor.vim))
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionSource == VimListenerManager.SelectionSource.OTHER && OptionConstants.selectmode_ideaselection in selectmode -> {
|
selectionSource == VimListenerManager.SelectionSource.OTHER && OptionConstants.selectmode_ideaselection in selectmode -> {
|
||||||
if (logReason) logger.debug("Enter select mode. Selection source is OTHER and selectMode has refactoring")
|
if (logReason) logger.debug("Enter select mode. Selection source is OTHER and selectMode has refactoring")
|
||||||
Mode.SELECT(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
|
Mode.SELECT(VimPlugin.getVisualMotion().detectSelectionType(editor.vim))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
if (logReason) logger.debug("Enter visual mode")
|
if (logReason) logger.debug("Enter visual mode")
|
||||||
Mode.VISUAL(VimPlugin.getVisualMotion().autodetectVisualSubmode(editor.vim))
|
Mode.VISUAL(VimPlugin.getVisualMotion().detectSelectionType(editor.vim))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
package com.maddyhome.idea.vim.group.visual
|
package com.maddyhome.idea.vim.group.visual
|
||||||
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode
|
||||||
|
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import java.awt.event.ActionEvent
|
import java.awt.event.ActionEvent
|
||||||
|
@ -18,13 +18,13 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
|
|||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
*/
|
*/
|
||||||
internal class VisualMotionGroup : VimVisualMotionGroupBase() {
|
internal class VisualMotionGroup : VimVisualMotionGroupBase() {
|
||||||
override fun autodetectVisualSubmode(editor: VimEditor): SelectionType {
|
override fun detectSelectionType(editor: VimEditor): SelectionType {
|
||||||
// IJ specific. See https://youtrack.jetbrains.com/issue/VIM-1924.
|
// IJ specific. See https://youtrack.jetbrains.com/issue/VIM-1924.
|
||||||
val project = editor.ij.project
|
val project = editor.ij.project
|
||||||
if (project != null && FindManager.getInstance(project).selectNextOccurrenceWasPerformed()) {
|
if (project != null && FindManager.getInstance(project).selectNextOccurrenceWasPerformed()) {
|
||||||
return SelectionType.CHARACTER_WISE
|
return SelectionType.CHARACTER_WISE
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.autodetectVisualSubmode(editor)
|
return super.detectSelectionType(editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user