mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
368 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
cc9385f2a9
|
|||
32a2384a46
|
|||
fdd850de5a
|
|||
0f7116b136
|
|||
db8f0251fb
|
|||
2ca2c1e774
|
|||
f3c32da4d1
|
|||
1a3b34d457
|
|||
1f9159996d
|
|||
65c3acd891
|
|||
223f65c003
|
|||
293b854620
|
|||
9e7c5fd603
|
|||
00c799595f
|
|||
8577b5ed20
|
|||
3af7a991a0
|
|||
212af1798d
|
|||
002ef8f72f
|
|||
9115af6b3d
|
|||
25ca42d371
|
|||
408687c9b3
|
|||
85e00bf8fc
|
|||
bd6f2d4b2f
|
|||
![]() |
e406885ec6 | ||
![]() |
9bbeab8062 | ||
![]() |
373bfc4eab | ||
![]() |
050f2f7b97 | ||
![]() |
e30bc14843 | ||
![]() |
76d590be11 | ||
![]() |
b005328b4a | ||
![]() |
ad20021cee | ||
![]() |
126de5c218 | ||
![]() |
0f7aef3f15 | ||
![]() |
f352b84922 | ||
![]() |
789faa7cb2 | ||
![]() |
a338f5768a | ||
![]() |
8205c74571 | ||
![]() |
def40eb409 | ||
![]() |
010e8a7541 | ||
![]() |
46c6778b3a | ||
![]() |
0977bd4400 | ||
![]() |
db092e9b0a | ||
![]() |
5cfb98e188 | ||
![]() |
3a95b62885 | ||
![]() |
93e6adf5a9 | ||
![]() |
37204398ff | ||
![]() |
b2f450d14d | ||
![]() |
61da888571 | ||
![]() |
fcda97cfb8 | ||
![]() |
1dc7ea6363 | ||
![]() |
bb507db884 | ||
![]() |
7b0482ed94 | ||
![]() |
1c79b0d59a | ||
![]() |
ff4eb31418 | ||
![]() |
cb1078cf70 | ||
![]() |
da3e40eaf6 | ||
![]() |
17f77a9639 | ||
![]() |
3d03494354 | ||
![]() |
642caddda7 | ||
![]() |
1d97c43e30 | ||
![]() |
7ed3e3b53c | ||
![]() |
aa39ca2006 | ||
![]() |
bb122903de | ||
![]() |
c84a3cf64d | ||
![]() |
f4c9464b8a | ||
![]() |
3ba14d05b4 | ||
![]() |
2189b70b87 | ||
![]() |
ea2222f9d5 | ||
![]() |
7d68d41888 | ||
![]() |
44749b6d8c | ||
![]() |
1ce4dbc569 | ||
![]() |
db59513505 | ||
![]() |
8c9ff9465f | ||
![]() |
1a2322ddec | ||
![]() |
fbbd1ebc0d | ||
![]() |
8d5df11372 | ||
![]() |
dfebe542d8 | ||
![]() |
7fde47c08b | ||
![]() |
629919f634 | ||
![]() |
43fff1c73e | ||
![]() |
d02f0e17ca | ||
![]() |
5dc860f61e | ||
![]() |
956e726c31 | ||
![]() |
446067e2fe | ||
![]() |
bd53a895c0 | ||
![]() |
e61fed2467 | ||
![]() |
f5e125759f | ||
![]() |
622eb887c8 | ||
![]() |
9fb614e16c | ||
![]() |
1e0fa07768 | ||
![]() |
bfb1d5b7f5 | ||
![]() |
2fad8790a9 | ||
![]() |
9719106a14 | ||
![]() |
870a0da2a2 | ||
![]() |
d3c315d299 | ||
![]() |
2583b6e792 | ||
![]() |
89417eb4f6 | ||
![]() |
560700c9aa | ||
![]() |
24514039e1 | ||
![]() |
ff44596c1a | ||
![]() |
b001d63fd9 | ||
![]() |
5db96bef28 | ||
![]() |
39c615cddd | ||
![]() |
961173a93b | ||
![]() |
92741c6356 | ||
![]() |
643eb2a85f | ||
![]() |
883744e4ee | ||
![]() |
66173e03be | ||
![]() |
e455722758 | ||
![]() |
823bdc1561 | ||
![]() |
f91fda2ca5 | ||
![]() |
92abd76615 | ||
![]() |
57c45ca153 | ||
![]() |
7c623ae4b5 | ||
![]() |
f2ef92cdef | ||
![]() |
e8e6eabe97 | ||
![]() |
ef1c915264 | ||
![]() |
a5e2168f7f | ||
![]() |
83e5470b3a | ||
![]() |
c446de8979 | ||
![]() |
13426915f4 | ||
![]() |
d766c3b8ee | ||
![]() |
844bc01537 | ||
![]() |
3d2d32b022 | ||
![]() |
a8677d3dd7 | ||
![]() |
7217fdf734 | ||
![]() |
0c867b3869 | ||
![]() |
b3bcab4336 | ||
![]() |
fe1b48a9b3 | ||
![]() |
b5a0862520 | ||
![]() |
babc1f54e5 | ||
![]() |
32b910a65b | ||
![]() |
2aa71a0008 | ||
![]() |
c2c0c2aba2 | ||
![]() |
6a10cf5e0d | ||
![]() |
90474a3a4f | ||
![]() |
be43f74bc6 | ||
![]() |
5916c42cd1 | ||
![]() |
a43c7ece32 | ||
![]() |
40c1070b1a | ||
![]() |
75ccdb2a4d | ||
![]() |
3de7b0ca78 | ||
![]() |
448e32a6cc | ||
![]() |
4a85058ba2 | ||
![]() |
7e28deb328 | ||
![]() |
f3767b53b7 | ||
![]() |
1026e27e64 | ||
![]() |
18d653a9ae | ||
![]() |
fcf4b44443 | ||
![]() |
907e44b1d7 | ||
![]() |
6c9b39a623 | ||
![]() |
a3cb093b42 | ||
![]() |
524e854c61 | ||
![]() |
5588c27037 | ||
![]() |
90d36eea98 | ||
![]() |
f4414de86c | ||
![]() |
d46102ccaf | ||
![]() |
82347f5f0d | ||
![]() |
c594f28acb | ||
![]() |
bf6517e58f | ||
![]() |
aec2f4c435 | ||
![]() |
8f905758d5 | ||
![]() |
80cc236f48 | ||
![]() |
e432a02a45 | ||
![]() |
d7894fa7f4 | ||
![]() |
853d7032f0 | ||
![]() |
5f9f57e1c0 | ||
![]() |
f4381c8216 | ||
![]() |
20eee7cae7 | ||
![]() |
33392c2148 | ||
![]() |
bb67564fbe | ||
![]() |
61ccbcd788 | ||
![]() |
1dbaa3be6d | ||
![]() |
872bc22830 | ||
![]() |
ce23ed814c | ||
![]() |
82cd534756 | ||
![]() |
673809d6b9 | ||
![]() |
cdbaf73b1e | ||
![]() |
7f911b7e72 | ||
![]() |
c03a2dfe7e | ||
![]() |
75935ce4d1 | ||
![]() |
f0b203409e | ||
![]() |
b3b369eb59 | ||
![]() |
96072982cf | ||
![]() |
de016fc445 | ||
![]() |
554d9b5f7b | ||
![]() |
149edefad5 | ||
![]() |
52d3840c83 | ||
![]() |
793677d4fd | ||
![]() |
2376ee4877 | ||
![]() |
68dcab6262 | ||
![]() |
f38fd3512c | ||
![]() |
e515278ba3 | ||
![]() |
9cc69e41ee | ||
![]() |
2109ff235c | ||
![]() |
d6910aa81d | ||
![]() |
8369391902 | ||
![]() |
f336807498 | ||
![]() |
14ba5d7126 | ||
![]() |
288394d25f | ||
![]() |
fb08b5fd65 | ||
![]() |
3465e11c3a | ||
![]() |
e07a16863e | ||
![]() |
64f7532510 | ||
![]() |
dd892e77fb | ||
![]() |
65aeeba521 | ||
![]() |
ca3e56d0d6 | ||
![]() |
bcbfb0dc32 | ||
![]() |
46a4a10e63 | ||
![]() |
d5bddd077f | ||
![]() |
e22fd263cc | ||
![]() |
f3902d0ae0 | ||
![]() |
f9213ee45d | ||
![]() |
281bc2573e | ||
![]() |
ae8c7f6bfa | ||
![]() |
c0419d6018 | ||
![]() |
ea98e50f65 | ||
![]() |
168174e383 | ||
![]() |
53cd4e1b88 | ||
![]() |
27e3561bb8 | ||
![]() |
9bb9cb13e3 | ||
![]() |
16455f7241 | ||
![]() |
82225aa519 | ||
![]() |
5f2baefc6c | ||
![]() |
cedcf39723 | ||
![]() |
4925d9aada | ||
![]() |
f3e6df32d0 | ||
![]() |
5aaa8752af | ||
![]() |
a1d214316c | ||
![]() |
8a1e3eb066 | ||
![]() |
75a417773f | ||
![]() |
b3b3ee4f21 | ||
![]() |
07b1db4b28 | ||
![]() |
dc775a0f22 | ||
![]() |
10228f953e | ||
![]() |
afceecadbe | ||
![]() |
b2a4e59571 | ||
![]() |
b0b944bbf3 | ||
![]() |
89a3d74b93 | ||
![]() |
f4eef04750 | ||
![]() |
e62c86b99f | ||
![]() |
82bd792da5 | ||
![]() |
a58c9065e6 | ||
![]() |
e8bf984b76 | ||
![]() |
23e1a3499f | ||
![]() |
6b4e4bacd7 | ||
![]() |
a84c04ca08 | ||
![]() |
e67c71e440 | ||
![]() |
5078ff9c7a | ||
![]() |
647510de5d | ||
![]() |
84e11e4236 | ||
![]() |
9538714af1 | ||
![]() |
ffd832d990 | ||
![]() |
8de2b8976b | ||
![]() |
a6aa26b5d9 | ||
![]() |
2505651c68 | ||
![]() |
e67c7b23ff | ||
![]() |
453cca3b0c | ||
![]() |
6cee04a4be | ||
![]() |
ae8b9b4773 | ||
![]() |
e748b7b265 | ||
![]() |
c2401ec013 | ||
![]() |
8073d7ecd0 | ||
![]() |
64f7859ba7 | ||
![]() |
f1b94d7026 | ||
![]() |
79653b6048 | ||
![]() |
b0e6b72281 | ||
![]() |
e6220e5e53 | ||
![]() |
3c064845b1 | ||
![]() |
736cb219ca | ||
![]() |
fb30e4e387 | ||
![]() |
74550ffa16 | ||
![]() |
d0a0672282 | ||
![]() |
16e92ddf60 | ||
![]() |
4d8e68d800 | ||
![]() |
bbebfaf32a | ||
![]() |
7e56331e47 | ||
![]() |
750db8e71c | ||
![]() |
4255ef68a3 | ||
![]() |
3313464214 | ||
![]() |
683ba32a15 | ||
![]() |
90a60155e5 | ||
![]() |
b25d06ed9e | ||
![]() |
706ae3dd91 | ||
![]() |
9b15ed8181 | ||
![]() |
f355bef36b | ||
![]() |
4391e69c48 | ||
![]() |
0710d80391 | ||
![]() |
cf41a3a76c | ||
![]() |
31b2cd872f | ||
![]() |
2b6945cbb2 | ||
![]() |
ae5f43918f | ||
![]() |
6b6bc2752e | ||
![]() |
4556adae3c | ||
![]() |
1b0886041b | ||
![]() |
16e18f3ca7 | ||
![]() |
ee0d67fbbb | ||
![]() |
450527f172 | ||
![]() |
135518ee39 | ||
![]() |
58715ecb5f | ||
![]() |
75e26b101d | ||
![]() |
6421a6face | ||
![]() |
948520f90a | ||
![]() |
0765118ce2 | ||
![]() |
efd4c7b617 | ||
![]() |
c5346fbece | ||
![]() |
fe8e8ccc3e | ||
![]() |
eae111bc2c | ||
![]() |
6a6c1dc6b4 | ||
![]() |
86bbb282ab | ||
![]() |
28aa156cb7 | ||
![]() |
a7814e69de | ||
![]() |
1452c116cf | ||
![]() |
23dfc4b339 | ||
![]() |
931d4be972 | ||
![]() |
7dceda587b | ||
![]() |
52a969074d | ||
![]() |
e7b87d31cf | ||
![]() |
5eb0fae08f | ||
![]() |
798d805a0f | ||
![]() |
0d4ba06e57 | ||
![]() |
4913b13a2d | ||
![]() |
b0bab992db | ||
![]() |
af5f4227b7 | ||
![]() |
fa6a694ea4 | ||
![]() |
1da7ffc052 | ||
![]() |
c673f5818c | ||
![]() |
ec78a87644 | ||
![]() |
69d14ddcf5 | ||
![]() |
f62819df00 | ||
![]() |
39a85b6bc2 | ||
![]() |
f76ae3e867 | ||
![]() |
3651e5f2f0 | ||
![]() |
89e016ef6c | ||
![]() |
e4996f4c4d | ||
![]() |
c44ed58142 | ||
![]() |
0091af2a41 | ||
![]() |
d1eea68719 | ||
![]() |
133aff7fd8 | ||
![]() |
efde94db7a | ||
![]() |
6ec072b34e | ||
![]() |
4027a21514 | ||
![]() |
3665b1ab00 | ||
![]() |
cf6b292f0c | ||
![]() |
507e4173d3 | ||
![]() |
abc3575d3e | ||
![]() |
2c0ff587e3 | ||
![]() |
26c87535d6 | ||
![]() |
6ac8e672be | ||
![]() |
04ee2dd1e7 | ||
![]() |
3106a98aee | ||
![]() |
73769a3472 | ||
![]() |
085e253d77 | ||
![]() |
b2af8f153e | ||
![]() |
37fb41fca8 | ||
![]() |
e2b05ab639 | ||
![]() |
354fd8fef0 | ||
![]() |
9b97867be1 | ||
![]() |
06685d1721 | ||
![]() |
ae4b88a06b | ||
![]() |
c83ecc46ed | ||
![]() |
c32050a208 | ||
![]() |
4a8c7227e6 | ||
![]() |
55e61a7094 | ||
![]() |
60977d05b6 | ||
![]() |
601747f720 | ||
![]() |
0c91bc3207 | ||
![]() |
f5cd2c173f |
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
fetch-depth: 300
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
|
||||
|
||||
- name: Update YouTrack
|
||||
run: ./gradlew updateYoutrackOnCommit
|
||||
run: ./gradlew --no-configuration-cache updateYoutrackOnCommit
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
||||
|
6
.github/workflows/integrationsTest.yml
vendored
6
.github/workflows/integrationsTest.yml
vendored
@@ -18,16 +18,16 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 300
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Run tests
|
||||
run: ./gradlew integrationsTest
|
||||
run: ./gradlew --no-configuration-cache integrationsTest
|
||||
env:
|
||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
4
.github/workflows/kover.yml
vendored
4
.github/workflows/kover.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 300
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
6
.github/workflows/mergePr.yml
vendored
6
.github/workflows/mergePr.yml
vendored
@@ -20,17 +20,17 @@ jobs:
|
||||
fetch-depth: 50
|
||||
# See end of file updateChangeslog.yml for explanation of this secret
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Update authors
|
||||
id: update_authors
|
||||
run: ./gradlew updateMergedPr -PprId=${{ github.event.number }}
|
||||
run: ./gradlew --no-configuration-cache updateMergedPr -PprId=${{ github.event.number }}
|
||||
env:
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
12
.github/workflows/runUiOctopusTests.yml
vendored
12
.github/workflows/runUiOctopusTests.yml
vendored
@@ -16,14 +16,14 @@ jobs:
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
# - 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 runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
gradle --no-configuration-cache runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: mv tests/ui-ij-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||
- name: Save report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
idea-sandbox-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
@@ -78,4 +78,4 @@ jobs:
|
||||
# with:
|
||||
# name: ui-test-fails-report-linux
|
||||
# path: |
|
||||
# ui-test-example/build/reports
|
||||
# ui-test-example/build/reports
|
||||
|
10
.github/workflows/runUiPyTests.yml
vendored
10
.github/workflows/runUiPyTests.yml
vendored
@@ -19,14 +19,14 @@ jobs:
|
||||
python-version: '3.10'
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
# - 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 :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
|
||||
gradle --no-configuration-cache :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: mv tests/ui-py-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||
- name: Save report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -49,4 +49,4 @@ jobs:
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-py-tests/build/reports
|
||||
sandbox-idea-log
|
||||
idea-sandbox-log
|
||||
|
12
.github/workflows/runUiTests.yml
vendored
12
.github/workflows/runUiTests.yml
vendored
@@ -16,14 +16,14 @@ jobs:
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
# - 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 runIdeForUiTests > build/reports/idea.log &
|
||||
gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: mv tests/ui-ij-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||
- name: Save report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
idea-sandbox-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
@@ -78,4 +78,4 @@ jobs:
|
||||
# with:
|
||||
# name: ui-test-fails-report-linux
|
||||
# path: |
|
||||
# ui-test-example/build/reports
|
||||
# ui-test-example/build/reports
|
||||
|
6
.github/workflows/updateAuthors.yml
vendored
6
.github/workflows/updateAuthors.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Update authors
|
||||
id: update_authors
|
||||
run: ./gradlew updateAuthors --stacktrace
|
||||
run: ./gradlew --no-configuration-cache updateAuthors --stacktrace
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
8
.github/workflows/updateChangelog.yml
vendored
8
.github/workflows/updateChangelog.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
|
||||
|
||||
- name: Update changelog
|
||||
run: ./gradlew updateChangelog
|
||||
run: ./gradlew --no-configuration-cache updateChangelog
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
|
||||
@@ -60,4 +60,4 @@ jobs:
|
||||
# dependabot updates. See mergeDependatobPR.yml file.
|
||||
# However, it turned out that GitHub accepts pushes from the actions as a PR and requires checks, that are always
|
||||
# false for pushing from actions.
|
||||
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227
|
||||
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227
|
||||
|
4
.github/workflows/updateFormatting.yml
vendored
4
.github/workflows/updateFormatting.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
fetch-depth: 50
|
||||
# See end of file updateChangeslog.yml for explanation of this secret
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
*.swp
|
||||
/.gradle/
|
||||
/.intellijPlatform/
|
||||
|
||||
/.idea/
|
||||
!/.idea/scopes
|
||||
@@ -10,6 +11,8 @@
|
||||
!/.idea/runConfigurations
|
||||
!/.idea/codeStyles
|
||||
!/.idea/vcs.xml
|
||||
!/.idea/misc.xml
|
||||
!/.idea/.name
|
||||
|
||||
**/build/
|
||||
**/out/
|
||||
@@ -22,11 +25,10 @@
|
||||
.teamcity/*.iml
|
||||
|
||||
# Generated by gradle task "generateGrammarSource"
|
||||
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
|
||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||
# Generated JSONs for lazy classloading
|
||||
/vim-engine/src/main/resources/ksp-generated
|
||||
/src/main/resources/ksp-generated
|
||||
|
||||
# Created by github automation
|
||||
settings.xml
|
||||
|
||||
.kotlin
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
IdeaVim
|
22
.idea/misc.xml
generated
Normal file
22
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<list size="3">
|
||||
<item index="0" class="java.lang.String" itemvalue="com.intellij.vim.annotations.CommandOrMotion" />
|
||||
<item index="1" class="java.lang.String" itemvalue="com.intellij.vim.annotations.ExCommand" />
|
||||
<item index="2" class="java.lang.String" itemvalue="com.intellij.vim.annotations.VimscriptFunction" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/.teamcity/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
@@ -12,7 +12,7 @@
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="check" />
|
||||
<option value="runPluginVerifier" />
|
||||
<option value="verifyPlugin" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
@@ -20,6 +20,7 @@
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
25
.idea/runConfigurations/Start_IJ_with_IdeaVim__Split_Mode_.xml
generated
Normal file
25
.idea/runConfigurations/Start_IJ_with_IdeaVim__Split_Mode_.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="runIdeSplitMode" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
2
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
2
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
@@ -37,7 +37,7 @@ object Compatibility : IdeaVimBuildType({
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
|
||||
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
|
||||
# java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
|
||||
""".trimIndent()
|
||||
|
2
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
2
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
@@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
|
||||
|
||||
steps {
|
||||
gradle {
|
||||
tasks = "clean runPluginVerifier"
|
||||
tasks = "clean verifyPlugin"
|
||||
buildFile = ""
|
||||
enableStacktrace = true
|
||||
}
|
||||
|
15
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
15
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@@ -19,8 +19,6 @@ 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 ReleaseMajor : ReleasePlugin("major")
|
||||
object ReleaseMinor : ReleasePlugin("minor")
|
||||
@@ -146,6 +144,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
gradle {
|
||||
name = "Run Integrations"
|
||||
tasks = "releaseActions"
|
||||
gradleParams = "--no-configuration-cache"
|
||||
}
|
||||
// gradle {
|
||||
// name = "Slack Notification"
|
||||
@@ -158,16 +157,4 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
24
AUTHORS.md
24
AUTHORS.md
@@ -511,6 +511,30 @@ Contributors:
|
||||
[![icon][github]](https://github.com/Aisper)
|
||||
|
||||
Egor Nikolaevsky
|
||||
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
|
||||
[![icon][github]](https://github.com/kun-codes)
|
||||
|
||||
Bishwa Saha,
|
||||
* [![icon][mail]](mailto:alexfu@fastmail.com)
|
||||
[![icon][github]](https://github.com/alexfu)
|
||||
|
||||
Alex Fu
|
||||
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
|
||||
[![icon][github]](https://github.com/LazyScaper)
|
||||
|
||||
Jake
|
||||
* [![icon][mail]](mailto:the1xdeveloper@gmail.com)
|
||||
[![icon][github]](https://github.com/The1xDeveloper)
|
||||
|
||||
The1xDeveloper
|
||||
* [![icon][mail]](mailto:shaunewilliams@gmail.com)
|
||||
[![icon][github]](https://github.com/shaunlebron)
|
||||
|
||||
shaun
|
||||
* [![icon][mail]](mailto:i.i.babko@gmail.com)
|
||||
[![icon][github]](https://github.com/igorbabko)
|
||||
|
||||
Igor Babko
|
||||
|
||||
Previous contributors:
|
||||
|
||||
|
@@ -27,8 +27,8 @@ usual beta standards.
|
||||
|
||||
Since version 2.9.0, the changelog can be found on YouTrack
|
||||
|
||||
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
|
||||
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
|
||||
* [To Be Released](https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20)
|
||||
* [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
|
||||
|
||||
## 2.9.0, 2024-02-20
|
||||
|
||||
|
@@ -62,12 +62,16 @@ for a few days or send it to a friend for testing.
|
||||
If you are looking for:
|
||||
|
||||
- Vim commands (`w`, `<C-O>`, `p`, etc.):
|
||||
- Any particular command: `package-info.java`.
|
||||
- Any particular command:
|
||||
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_commands.json)
|
||||
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_commands.json)
|
||||
- How commands are executed in common: `EditorActionHandlerBase`.
|
||||
- Key mapping: `KeyHandler.handleKey()`.
|
||||
|
||||
- Ex commands (`:set`, `:s`, `:nohlsearch`):
|
||||
- Any particular ex command: package `com.maddyhome.idea.vim.ex.handler`.
|
||||
- Any particular command:
|
||||
- [Commands common for Fleet and IdeaVim](vim-engine/src/main/resources/ksp-generated/engine_ex_commands.json)
|
||||
- [IdeaVim only commands](src/main/resources/ksp-generated/intellij_ex_commands.json)
|
||||
- Vim script grammar: `Vimscript.g4`.
|
||||
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
|
||||
- Vim script executor: `Executor`.
|
||||
|
@@ -109,7 +109,6 @@ etc
|
||||
|
||||
See also:
|
||||
|
||||
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
|
||||
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
|
||||
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
|
||||
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
|
||||
@@ -222,13 +221,13 @@ Ex commands or via `:map` command mappings:
|
||||
* Execute an action by `{action_id}`. Works from Ex command line.
|
||||
* Please don't use `:action` in mappings. Use `<Action>` instead.
|
||||
|
||||
### Finding action ids:
|
||||
### Finding action IDs:
|
||||
|
||||
* IJ provides `IdeaVim: track action Ids` command to show the id of the executed actions.
|
||||
* IJ provides `IdeaVim: track action IDs` command to show the id of the executed actions.
|
||||
This command can be found in "Search everywhere" (double `shift`).
|
||||
|
||||
<details>
|
||||
<summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
|
||||
<summary><strong>"Track action IDs" Details</strong> (click to see)</summary>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
|
||||
<img src="assets/readme/track_action_light.gif" alt="track action ids"/>
|
||||
@@ -369,6 +368,8 @@ is the full list of synonyms.
|
||||
- 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).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
IdeaVim project is licensed under MIT license except the following parts of it:
|
||||
|
||||
* File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.
|
||||
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
|
||||
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization") version "1.9.22"
|
||||
kotlin("plugin.serialization") version "2.0.0"
|
||||
}
|
||||
|
||||
val kotlinxSerializationVersion: String by project
|
||||
@@ -21,7 +21,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20")
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.24")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
||||
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||
|
@@ -37,7 +37,8 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
|
||||
Files.createDirectories(generatedDirPath)
|
||||
|
||||
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
|
||||
val fileContent = json.encodeToString(commands)
|
||||
val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
|
||||
val fileContent = json.encodeToString(sortedCommands)
|
||||
filePath.writeText(fileContent)
|
||||
|
||||
return emptyList()
|
||||
|
@@ -37,7 +37,8 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
|
||||
Files.createDirectories(generatedDirPath)
|
||||
|
||||
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
|
||||
val fileContent = json.encodeToString(commandToClass)
|
||||
val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
|
||||
val fileContent = json.encodeToString(sortedCommandToClass)
|
||||
filePath.writeText(fileContent)
|
||||
|
||||
return emptyList()
|
||||
|
@@ -37,7 +37,8 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
|
||||
Files.createDirectories(generatedDirPath)
|
||||
|
||||
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
|
||||
val fileContent = json.encodeToString(nameToClass)
|
||||
val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
|
||||
val fileContent = json.encodeToString(sortedNameToClass)
|
||||
filePath.writeText(fileContent)
|
||||
|
||||
return emptyList()
|
||||
|
@@ -13,7 +13,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||
import com.intellij.vim.processors.VimscriptFunctionProcessor
|
||||
|
||||
public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
||||
class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
||||
return VimscriptFunctionProcessor(environment)
|
||||
}
|
||||
|
312
build.gradle.kts
312
build.gradle.kts
@@ -32,6 +32,8 @@ import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.RepositoryBuilder
|
||||
import org.intellij.markdown.ast.getTextInNode
|
||||
import org.jetbrains.changelog.Changelog
|
||||
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
|
||||
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
|
||||
import org.kohsuke.github.GHUser
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
@@ -43,19 +45,19 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
|
||||
classpath("com.github.AlexPl292:mark-down-to-slack:1.1.2")
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
||||
classpath("org.kohsuke:github-api:1.305")
|
||||
|
||||
classpath("io.ktor:ktor-client-core:2.3.11")
|
||||
classpath("io.ktor:ktor-client-core:2.3.12")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.10")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.11")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.12")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
||||
|
||||
// This comes from the changelog plugin
|
||||
// classpath("org.jetbrains:markdown:0.3.1")
|
||||
@@ -63,45 +65,27 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
antlr
|
||||
java
|
||||
kotlin("jvm") version "1.9.22"
|
||||
kotlin("jvm") version "2.0.0"
|
||||
application
|
||||
id("java-test-fixtures")
|
||||
|
||||
id("org.jetbrains.intellij") version "1.17.3"
|
||||
id("org.jetbrains.changelog") version "2.2.0"
|
||||
|
||||
id("org.jetbrains.intellij.platform") version "2.0.0-rc2"
|
||||
id("org.jetbrains.changelog") version "2.2.1"
|
||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||
id("com.dorongold.task-tree") version "3.0.0"
|
||||
|
||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
||||
id("com.dorongold.task-tree") version "4.0.0"
|
||||
id("com.google.devtools.ksp") version "2.0.0-1.0.23"
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
||||
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
||||
arg("ex_commands_file", "intellij_ex_commands.json")
|
||||
arg("commands_file", "intellij_commands.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
||||
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestKotlin").configure { enabled = false }
|
||||
}
|
||||
val moduleSources by configurations.registering
|
||||
|
||||
// Import variables from gradle.properties file
|
||||
val javaVersion: String by project
|
||||
val kotlinVersion: String by project
|
||||
val ideaVersion: String by project
|
||||
val ideaType: String by project
|
||||
val downloadIdeaSources: String by project
|
||||
val instrumentPluginCode: String by project
|
||||
val antlrVersion: String by project
|
||||
val remoteRobotVersion: String by project
|
||||
val splitModeVersion: String by project
|
||||
|
||||
val publishChannels: String by project
|
||||
val publishToken: String by project
|
||||
@@ -109,25 +93,45 @@ val publishToken: String by project
|
||||
val slackUrl: String by project
|
||||
val youtrackToken: String by project
|
||||
|
||||
val releaseType: String? by project
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
|
||||
intellijPlatform {
|
||||
defaultRepositories()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":vim-engine"))
|
||||
ksp(project(":annotation-processors"))
|
||||
implementation(project(":annotation-processors"))
|
||||
compileOnly(project(":annotation-processors"))
|
||||
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
||||
compileOnly("org.jetbrains:annotations:24.1.0")
|
||||
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
|
||||
antlr("org.antlr:antlr4:$antlrVersion")
|
||||
|
||||
intellijPlatform {
|
||||
// Note that it is also possible to use local("...") to compile against a locally installed IDE
|
||||
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
|
||||
// Or something like: intellijIdeaUltimate(ideaVersion)
|
||||
create(ideaType, ideaVersion)
|
||||
|
||||
pluginVerifier()
|
||||
zipSigner()
|
||||
instrumentationTools()
|
||||
|
||||
testFramework(TestFrameworkType.Platform)
|
||||
testFramework(TestFrameworkType.JUnit5)
|
||||
|
||||
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
|
||||
plugin("AceJump", "3.8.19")
|
||||
plugin("com.intellij.classic.ui", "242.20224.159")
|
||||
}
|
||||
|
||||
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
|
||||
|
||||
// --------- Test dependencies ----------
|
||||
|
||||
testImplementation(testFixtures(project(":")))
|
||||
|
||||
testApi("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
|
||||
@@ -141,14 +145,20 @@ dependencies {
|
||||
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
||||
|
||||
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
|
||||
// Can be removed when IJPL-159134 is fixed
|
||||
// testRuntimeOnly("junit:junit:4.13.2")
|
||||
testImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
|
||||
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
|
||||
}
|
||||
|
||||
configurations {
|
||||
@@ -159,6 +169,8 @@ configurations {
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
// Set teamcity env variable locally to run additional tests for leaks.
|
||||
// By default, this test runs on TC only, but this test doesn't take a lot of time,
|
||||
// so we can turn it on for local development
|
||||
@@ -171,6 +183,9 @@ tasks {
|
||||
}
|
||||
|
||||
compileJava {
|
||||
// CodeQL can't resolve the 'by project' property, so we need to give it a hint. This is the minimum version we need
|
||||
// so doesn't have to match exactly
|
||||
// Hint for the CodeQL autobuilder: sourceCompatibility = 17
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
|
||||
@@ -183,35 +198,95 @@ tasks {
|
||||
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
|
||||
// For the list of bundled versions
|
||||
apiVersion = "1.9"
|
||||
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
|
||||
freeCompilerArgs = listOf(
|
||||
"-Xjvm-default=all-compatibility",
|
||||
|
||||
// Needed to compile the AceJump which uses kotlin beta
|
||||
// Without these two option compilation fails
|
||||
"-Xskip-prerelease-check",
|
||||
"-Xallow-unstable-dependencies",
|
||||
)
|
||||
// allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
enabled = false
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = javaVersion
|
||||
apiVersion = "1.9"
|
||||
|
||||
// Needed to compile the AceJump which uses kotlin beta
|
||||
// Without these two option compilation fails
|
||||
freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
|
||||
// allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
downloadRobotServerPlugin {
|
||||
version.set(remoteRobotVersion)
|
||||
}
|
||||
|
||||
runIdeForUiTests {
|
||||
systemProperty("robot-server.port", "8082")
|
||||
systemProperty("ide.mac.message.dialogs.as.sheets", "false")
|
||||
systemProperty("jb.privacy.policy.text", "<!--999.999-->")
|
||||
systemProperty("jb.consents.confirmation.enabled", "false")
|
||||
systemProperty("ide.show.tips.on.startup.default.value", "false")
|
||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||
}
|
||||
|
||||
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
|
||||
// a custom task (see below)
|
||||
runIde {
|
||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||
}
|
||||
|
||||
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
|
||||
// Note that the version must be greater than the plugin's target version, for obvious reasons
|
||||
// val runIdeCustom by intellijPlatformTesting.runIde.registering {
|
||||
// type = IntelliJPlatformType.Rider
|
||||
// version = "2024.1.2"
|
||||
// }
|
||||
|
||||
// Uncomment to run the plugin in a locally installed IDE
|
||||
// val runIdeLocal by intellijPlatformTesting.runIde.registering {
|
||||
// localPath = file("/Users/{user}/Applications/WebStorm.app")
|
||||
// }
|
||||
|
||||
val runIdeForUiTests by intellijPlatformTesting.runIde.registering {
|
||||
task {
|
||||
jvmArgumentProviders += CommandLineArgumentProvider {
|
||||
listOf(
|
||||
"-Drobot-server.port=8082",
|
||||
"-Dide.mac.message.dialogs.as.sheets=false",
|
||||
"-Djb.privacy.policy.text=<!--999.999-->",
|
||||
"-Djb.consents.confirmation.enabled=false",
|
||||
"-Dide.show.tips.on.startup.default.value=false",
|
||||
"-Doctopus.handler=" + (System.getProperty("octopus.handler") ?: true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
robotServerPlugin(remoteRobotVersion)
|
||||
}
|
||||
}
|
||||
|
||||
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
|
||||
splitMode = true
|
||||
splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
|
||||
|
||||
// Frontend split mode support requires 242+
|
||||
// TODO: Remove this once IdeaVim targets 242, as the task will naturally use the target version to run
|
||||
version.set(splitModeVersion)
|
||||
}
|
||||
|
||||
// Add plugin open API sources to the plugin ZIP
|
||||
val sourcesJar by registering(Jar::class) {
|
||||
dependsOn(moduleSources)
|
||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||
archiveClassifier.set(DocsType.SOURCES)
|
||||
from(sourceSets.main.map { it.kotlin })
|
||||
from(provider {
|
||||
moduleSources.map {
|
||||
it.map { jarFile -> zipTree(jarFile) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
buildPlugin {
|
||||
dependsOn(sourcesJar)
|
||||
from(sourcesJar) { into("lib/src") }
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -221,7 +296,6 @@ java {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion))
|
||||
}
|
||||
@@ -236,96 +310,68 @@ gradle.projectsEvaluated {
|
||||
|
||||
// --- Intellij plugin
|
||||
|
||||
intellij {
|
||||
version.set(ideaVersion)
|
||||
type.set(ideaType)
|
||||
pluginName.set("IdeaVim")
|
||||
intellijPlatform {
|
||||
pluginConfiguration {
|
||||
name = "IdeaVim"
|
||||
changeNotes.set(
|
||||
"""
|
||||
Undo in IdeaVim now works like in Vim<br/>
|
||||
Caret movement is no longer a separate undo step, and full insert is undoable in one step.<br/>
|
||||
<a href="https://youtrack.jetbrains.com/issue/VIM-547/Undo-splits-Insert-mode-edits-into-separate-undo-chunks">Share Feedback</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
updateSinceUntilBuild.set(false)
|
||||
ideaVersion {
|
||||
// Let the Gradle plugin set the since-build version. It defaults to the version of the IDE we're building against
|
||||
// specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified.
|
||||
// The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it
|
||||
// with a null provider (the provider is important).
|
||||
// By letting the Gradle plugin handle this, the Plugin DevKit IntelliJ plugin cannot help us with the "Usage of
|
||||
// IntelliJ API not available in older IDEs" inspection. However, since our since-build is the version we compile
|
||||
// against, we can never get an API that's newer - it would be an unresolved symbol.
|
||||
untilBuild.set(provider { null })
|
||||
}
|
||||
}
|
||||
|
||||
downloadSources.set(downloadIdeaSources.toBoolean())
|
||||
instrumentCode.set(instrumentPluginCode.toBoolean())
|
||||
intellijRepository.set("https://www.jetbrains.com/intellij-repository")
|
||||
plugins.set(listOf("AceJump:3.8.11"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
publishPlugin {
|
||||
publishing {
|
||||
channels.set(publishChannels.split(","))
|
||||
token.set(publishToken)
|
||||
}
|
||||
|
||||
signPlugin {
|
||||
signing {
|
||||
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
|
||||
privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
|
||||
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
|
||||
}
|
||||
|
||||
runPluginVerifier {
|
||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
||||
teamCityOutputFormat.set(true)
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
maxHeapSize = "128m"
|
||||
arguments.addAll(listOf("-package", "com.maddyhome.idea.vim.vimscript.parser.generated", "-visitor"))
|
||||
outputDirectory = file("src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated")
|
||||
}
|
||||
|
||||
named("compileKotlin") {
|
||||
dependsOn("generateGrammarSource")
|
||||
}
|
||||
named("compileTestKotlin") {
|
||||
dependsOn("generateTestGrammarSource")
|
||||
}
|
||||
named("compileTestFixturesKotlin") {
|
||||
dependsOn("generateTestFixturesGrammarSource")
|
||||
}
|
||||
|
||||
// Add plugin open API sources to the plugin ZIP
|
||||
val createOpenApiSourceJar by registering(Jar::class) {
|
||||
dependsOn("generateGrammarSource")
|
||||
// Java sources
|
||||
from(sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
verifyPlugin {
|
||||
teamCityOutputFormat = true
|
||||
ides {
|
||||
recommended()
|
||||
}
|
||||
from(project(":vim-engine").sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
}
|
||||
// Kotlin sources
|
||||
from(kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||
archiveClassifier.set("src")
|
||||
}
|
||||
|
||||
buildPlugin {
|
||||
dependsOn(createOpenApiSourceJar)
|
||||
from(createOpenApiSourceJar) { into("lib/src") }
|
||||
}
|
||||
|
||||
patchPluginXml {
|
||||
// Don't forget to update plugin.xml
|
||||
sinceBuild.set("241.15989.150")
|
||||
|
||||
changeNotes.set(
|
||||
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
|
||||
)
|
||||
}
|
||||
instrumentCode.set(instrumentPluginCode.toBoolean())
|
||||
}
|
||||
|
||||
// --- Tests
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
ksp {
|
||||
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
||||
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
||||
arg("ex_commands_file", "intellij_ex_commands.json")
|
||||
arg("commands_file", "intellij_commands.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestKotlin").configure { enabled = false }
|
||||
}
|
||||
|
||||
|
||||
// --- Changelog
|
||||
|
||||
changelog {
|
||||
@@ -451,6 +497,8 @@ val fixVersionsElementType = "VersionBundleElement"
|
||||
tasks.register("releaseActions") {
|
||||
group = "other"
|
||||
doLast {
|
||||
if (releaseType == "patch") return@doLast
|
||||
|
||||
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
|
||||
if (tickets.isNotEmpty()) {
|
||||
println("Updating statuses for tickets: $tickets")
|
||||
@@ -910,12 +958,12 @@ fun changes(): List<Change> {
|
||||
println("Start changes processing")
|
||||
for (message in messages) {
|
||||
println("Processing '$message'...")
|
||||
val lowercaseMessage = message.toLowerCase()
|
||||
val lowercaseMessage = message.lowercase()
|
||||
val regex = "^fix\\((vim-\\d+)\\):".toRegex()
|
||||
val findResult = regex.find(lowercaseMessage)
|
||||
if (findResult != null) {
|
||||
println("Message matches")
|
||||
val value = findResult.groups[1]!!.value.toUpperCase()
|
||||
val value = findResult.groups[1]!!.value.uppercase()
|
||||
val shortMessage = message.drop(findResult.range.last + 1).trim()
|
||||
newFixes += Change(value, shortMessage)
|
||||
} else {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
Welcome to the IdeaVim wiki!
|
||||
|
||||
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins.md)
|
||||
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples.md)
|
||||
- List of "set" commands: ["set" commands](set-commands.md)
|
||||
- Docs about "select" mode in vim: [select mode](Select-mode.md)
|
||||
- List of IdeaVim plugins: [plugins](IdeaVim%20Plugins)
|
||||
- Examples of `ideajoin` option (also known as "smart join"): ["ideajoin" examples](ideajoin-examples)
|
||||
- List of "set" commands: ["set" commands](set-commands)
|
||||
- Docs about "select" mode in vim: [select mode](Select-mode)
|
||||
|
@@ -82,7 +82,7 @@ Original plugin: [NERDTree](https://github.com/preservim/nerdtree).
|
||||
|
||||
### Instructions
|
||||
|
||||
[See here](NERDTree-support.md).
|
||||
[See here](NERDTree-support).
|
||||
|
||||
</details>
|
||||
|
||||
@@ -129,8 +129,26 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
|
||||
</details>
|
||||
|
||||
### Instructions
|
||||
|
||||
https://github.com/terryma/vim-multiple-cursors/blob/master/doc/multiple_cursors.txt
|
||||
|
||||
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
|
||||
|
||||
```
|
||||
" Remap multiple-cursors shortcuts to match terryma/vim-multiple-cursors
|
||||
nmap <C-n> <Plug>NextWholeOccurrence
|
||||
xmap <C-n> <Plug>NextWholeOccurrence
|
||||
nmap g<C-n> <Plug>NextOccurrence
|
||||
xmap g<C-n> <Plug>NextOccurrence
|
||||
xmap <C-x> <Plug>SkipOccurrence
|
||||
xmap <C-p> <Plug>RemoveOccurrence
|
||||
|
||||
" Note that the default <A-n> and g<A-n> shortcuts don't work on Mac due to dead keys.
|
||||
" <A-n> is used to enter accented text e.g. ñ
|
||||
" Feel free to pick your own mappings that are not affected. I like to use <leader>
|
||||
nmap <leader><C-n> <Plug>AllWholeOccurrences
|
||||
xmap <leader><C-n> <Plug>AllWholeOccurrences
|
||||
nmap <leader>g<C-n> <Plug>AllOccurrences
|
||||
xmap <leader>g<C-n> <Plug>AllOccurrences
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
@@ -40,30 +40,33 @@ Plug 'nerdtree'
|
||||
- `:NERDTreeFind`
|
||||
- `:NERDTreeRefreshRoot`
|
||||
|
||||
| Key | Description | Map Setting |
|
||||
|---------|---------------------------------------------------------|--------------------------------|
|
||||
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
||||
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
||||
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
||||
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
||||
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
||||
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
||||
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
||||
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
||||
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
||||
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
||||
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
||||
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
||||
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
||||
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
||||
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
||||
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
||||
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
||||
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
||||
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
||||
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
||||
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
||||
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
||||
| Key | Description | Map Setting |
|
||||
|---------|--------------------------------------------------------|--------------------------------|
|
||||
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
||||
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
||||
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
||||
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
||||
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
||||
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
||||
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
||||
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
||||
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
||||
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
||||
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
||||
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
||||
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
||||
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
||||
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
||||
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
||||
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
||||
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
||||
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
||||
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
||||
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
||||
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
||||
| `d` | Delete file or directory | `g:NERDTreeMapDelete` |
|
||||
| `n` | Create File | `g:NERDTreeMapNewFile` |
|
||||
| `N` | Create Directory | `g:NERDTreeMapNewDir` |
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
@@ -5,9 +5,9 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
|
||||
1. Install the plugin via Marketplace
|
||||
2. Enable action tracking. You can enable it by one of the following ways:
|
||||
* Execute `:set trackactionids` ex command or just `:set tai`
|
||||
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action Ids" to find the toggle that enables and disables action tracking
|
||||
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action IDs" to find the toggle that enables and disables action tracking
|
||||
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
|
||||
|
||||
<img alt="Notification" src="images/action-id-notification.png"/>
|
||||
4. Copy the action id from the notification to create the following mapping in your .ideavimrc
|
||||
```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)```
|
||||
```map <leader>t <Action>(de.netnexus.CamelCasePlugin.ToggleCamelCase)```
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Support Guide
|
||||
|
||||
This document is created to help our support team.
|
||||
It's not intended to be read by the users as it brings to value to them.
|
||||
It's not intended to be read by the users as it brings no value to them.
|
||||
|
||||
## Support channels
|
||||
|
||||
@@ -35,4 +35,4 @@ IdeaVim has multiple YouTrack statuses, main are:
|
||||
# ~.ideavimrc file
|
||||
|
||||
`~/.ideavimrc` is the file that is used for IdeaVim configuration. It may affect behaviour of the program,
|
||||
so it makes sense to additionally request this file in case the issues can't be reproduced.
|
||||
so it makes sense to additionally request this file in case the issues can't be reproduced.
|
||||
|
@@ -8,22 +8,33 @@
|
||||
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||
ideaVersion=2024.1.1
|
||||
# ideaVersion is the version of the IDE that will be added as a compile-time dependency. The format can be either
|
||||
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
|
||||
# resolved against the configured repositories, which by default includes Maven releases and snapshots, the CDN used to
|
||||
# download consumer releases, the plugin marketplace and so on.
|
||||
# You can find an example list of all CDN based versions for IDEA Community here:
|
||||
# https://data.services.jetbrains.com/products?code=IC
|
||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
||||
ideaVersion=2024.2
|
||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||
ideaType=IC
|
||||
downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=chylex-35
|
||||
version=chylex-40
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.22
|
||||
remoteRobotVersion=0.11.23
|
||||
antlrVersion=4.10.1
|
||||
|
||||
kotlin.incremental.useClasspathSnapshot=false
|
||||
# [VERSION UPDATE] 2024.2 - remove when IdeaVim targets 2024.2
|
||||
# Running IdeaVim in split mode requires 242. Update this version once 242 has been released, and remove it completely
|
||||
# when IdeaVim targets 242, at which point runIdeSplitMode will run correctly with the target version.
|
||||
# See also runIdeSplitMode
|
||||
splitModeVersion=242-EAP-SNAPSHOT
|
||||
|
||||
|
||||
# Please don't forget to update kotlin version in buildscript section
|
||||
# Also update kotlinxSerializationVersion version
|
||||
kotlinVersion=1.9.22
|
||||
kotlinVersion=2.0.0
|
||||
publishToken=token
|
||||
publishChannels=eap
|
||||
|
||||
@@ -36,6 +47,7 @@ youtrackToken=
|
||||
|
||||
# Gradle settings
|
||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||
org.gradle.caching=true
|
||||
|
||||
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
|
||||
kotlin.stdlib.default.dependency=false
|
||||
|
@@ -19,9 +19,8 @@ exclude:
|
||||
- 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
|
||||
- src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||
- src/main/java/com/maddyhome/idea/vim/package-info.java
|
||||
- vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||
- 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:
|
||||
|
@@ -20,17 +20,17 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.25")
|
||||
|
||||
implementation("io.ktor:ktor-client-core:2.3.11")
|
||||
implementation("io.ktor:ktor-client-core:2.3.12")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.10")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.11")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.12")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.9.0.202403050737-r")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
||||
implementation("com.vdurmont:semver4j:3.1.0")
|
||||
}
|
||||
|
||||
|
@@ -1,20 +0,0 @@
|
||||
// Set repository for snapshot versions of gradle plugin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'IdeaVIM'
|
||||
include 'vim-engine'
|
||||
include 'scripts'
|
||||
include 'annotation-processors'
|
||||
include 'tests:java-tests'
|
||||
include 'tests:property-tests'
|
||||
include 'tests:long-running-tests'
|
||||
include 'tests:ui-ij-tests'
|
||||
include 'tests:ui-py-tests'
|
||||
include 'tests:ui-fixtures'
|
21
settings.gradle.kts
Normal file
21
settings.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Set repository for snapshot versions of gradle plugin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "IdeaVIM"
|
||||
|
||||
include("vim-engine")
|
||||
include("scripts")
|
||||
include("annotation-processors")
|
||||
include("tests:java-tests")
|
||||
include("tests:property-tests")
|
||||
include("tests:long-running-tests")
|
||||
include("tests:ui-ij-tests")
|
||||
include("tests:ui-py-tests")
|
||||
include("tests:ui-fixtures")
|
@@ -8,14 +8,19 @@
|
||||
|
||||
package com.maddyhome.idea.vim
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManagerListener
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.updateSettings.impl.UpdateSettings
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.newapi.initInjector
|
||||
import com.maddyhome.idea.vim.ui.JoinEap
|
||||
import com.maddyhome.idea.vim.ui.JoinEap.EAP_LINK
|
||||
|
||||
/**
|
||||
* @author Alex Plate
|
||||
@@ -28,6 +33,11 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
||||
if (firstInitializationOccurred) return
|
||||
firstInitializationOccurred = true
|
||||
|
||||
if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
|
||||
VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
|
||||
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
|
||||
}
|
||||
|
||||
// This code should be executed once
|
||||
VimPlugin.getInstance().initialize()
|
||||
}
|
||||
@@ -36,6 +46,7 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
||||
// This is a temporal workaround for VIM-2487
|
||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
|
||||
override fun projectClosingBeforeSave(project: Project) {
|
||||
initInjector()
|
||||
// TODO: Confirm context in CWM scenario
|
||||
if (injector.globalIjOptions().closenotebooks) {
|
||||
injector.editorGroup.getEditors().forEach { vimEditor ->
|
||||
|
@@ -14,36 +14,36 @@ import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
public object RegisterActions {
|
||||
object RegisterActions {
|
||||
/**
|
||||
* Register all the key/action mappings for the plugin.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun registerActions() {
|
||||
fun registerActions() {
|
||||
registerVimCommandActions()
|
||||
registerShortcutsWithoutActions()
|
||||
}
|
||||
|
||||
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
fun findAction(id: String): EditorActionHandlerBase? {
|
||||
val commandBean = IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
return commandBean.instance
|
||||
}
|
||||
|
||||
public fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||
fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||
return findAction(id) ?: throw RuntimeException("Action $id is not registered")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun unregisterActions() {
|
||||
fun unregisterActions() {
|
||||
val keyGroup = VimPlugin.getKeyIfCreated()
|
||||
keyGroup?.unregisterCommandActions()
|
||||
}
|
||||
|
||||
private fun registerVimCommandActions() {
|
||||
val parser = VimPlugin.getKey()
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
}
|
||||
|
||||
private fun registerShortcutsWithoutActions() {
|
||||
|
@@ -37,8 +37,9 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
|
||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
||||
import com.maddyhome.idea.vim.yank.YankGroupBase;
|
||||
import org.jdom.Element;
|
||||
@@ -46,6 +47,7 @@ import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
|
||||
@@ -66,7 +68,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
||||
|
||||
static {
|
||||
VimInjectorKt.setInjector(new IjVimInjector());
|
||||
IjVimInjectorKt.initInjector();
|
||||
}
|
||||
|
||||
private final @NotNull VimState state = new VimState();
|
||||
@@ -123,12 +125,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return (FileGroup)VimInjectorKt.getInjector().getFile();
|
||||
}
|
||||
|
||||
public static @NotNull SearchGroup getSearch() {
|
||||
return ApplicationManager.getApplication().getService(SearchGroup.class);
|
||||
public static @NotNull IjVimSearchGroup getSearch() {
|
||||
return ApplicationManager.getApplication().getService(IjVimSearchGroup.class);
|
||||
}
|
||||
|
||||
public static @Nullable SearchGroup getSearchIfCreated() {
|
||||
return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
|
||||
public static @Nullable IjVimSearchGroup getSearchIfCreated() {
|
||||
return ApplicationManager.getApplication().getServiceIfCreated(IjVimSearchGroup.class);
|
||||
}
|
||||
|
||||
public static @NotNull ProcessGroup getProcess() {
|
||||
@@ -283,11 +285,11 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
try {
|
||||
VimInjectorKt.injector.getOptionGroup().startInitVimRc();
|
||||
injector.getOptionGroup().startInitVimRc();
|
||||
executeIdeaVimRc(editor);
|
||||
}
|
||||
finally {
|
||||
VimInjectorKt.injector.getOptionGroup().endInitVimRc();
|
||||
injector.getOptionGroup().endInitVimRc();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,14 +347,14 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
}
|
||||
|
||||
private void turnOffPlugin(boolean unsubscribe) {
|
||||
SearchGroup searchGroup = getSearchIfCreated();
|
||||
IjVimSearchGroup searchGroup = getSearchIfCreated();
|
||||
if (searchGroup != null) {
|
||||
searchGroup.turnOff();
|
||||
}
|
||||
if (unsubscribe) {
|
||||
VimListenerManager.INSTANCE.turnOff();
|
||||
}
|
||||
ExEntryPanel.fullReset();
|
||||
injector.getCommandLine().fullReset();
|
||||
|
||||
// Unregister vim actions in command mode
|
||||
RegisterActions.unregisterActions();
|
||||
|
@@ -12,13 +12,13 @@ import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class VimProjectService(val project: Project) : Disposable {
|
||||
override fun dispose() {
|
||||
// Not sure if this is a best solution
|
||||
EditorHolderService.getInstance().editor = null
|
||||
ExEntryPanel.getInstance().setEditor(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@@ -32,7 +32,7 @@ import javax.swing.KeyStroke
|
||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||
* way to get ideavim keys for this plugin. See VIM-3085
|
||||
*/
|
||||
public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
||||
class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
||||
private val handler = KeyHandler.getInstance()
|
||||
private val traceTime = injector.globalOptions().ideatracetime
|
||||
|
||||
|
@@ -8,6 +8,6 @@
|
||||
|
||||
package com.maddyhome.idea.vim.action
|
||||
|
||||
public object IntellijCommandProvider : CommandProvider {
|
||||
object IntellijCommandProvider : CommandProvider {
|
||||
override val commandListFileName: String = "intellij_commands.json"
|
||||
}
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||
import com.maddyhome.idea.vim.group.IjOptionConstants
|
||||
import com.maddyhome.idea.vim.group.IjOptions
|
||||
import com.maddyhome.idea.vim.handler.enableOctopus
|
||||
@@ -44,7 +43,9 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
|
||||
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.initInjector
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import java.awt.event.InputEvent
|
||||
@@ -60,11 +61,14 @@ import javax.swing.KeyStroke
|
||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||
* way to get ideavim keys for this plugin. See VIM-3085
|
||||
*/
|
||||
public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||
|
||||
init {
|
||||
initInjector()
|
||||
}
|
||||
|
||||
private val traceTime: Boolean
|
||||
get() {
|
||||
// Make sure the injector is initialized
|
||||
VimPlugin.getInstance()
|
||||
return injector.globalOptions().ideatracetime
|
||||
}
|
||||
|
||||
@@ -257,7 +261,7 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
||||
private fun getEditor(e: AnActionEvent): Editor? {
|
||||
return e.getData(PlatformDataKeys.EDITOR)
|
||||
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
|
||||
EditorHolderService.getInstance().editor
|
||||
ExEntryPanel.getInstance().ijEditor
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.helper.inRepeatMode
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||
@@ -102,7 +102,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||
val argument = cmd.argument ?: return false
|
||||
if (!editor.vimStateMachine.isDotRepeatInProgress) {
|
||||
if (!editor.inRepeatMode) {
|
||||
argumentCaptured = argument
|
||||
}
|
||||
val range = getMotionRange(editor, context, argument, operatorArguments)
|
||||
|
@@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
|
||||
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
|
||||
@@ -25,7 +24,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_WRITABLE
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||
val state = editor.vimStateMachine
|
||||
val state = injector.vimState
|
||||
val lastCommand = VimRepeater.lastChangeCommand
|
||||
|
||||
if (lastCommand == null && Extension.lastExtensionHandler == null) return false
|
||||
|
@@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
|
||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
|
||||
public class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
||||
class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
override fun runAsMulticaret(
|
||||
editor: VimEditor,
|
||||
|
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
|
||||
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
|
||||
public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun execute(
|
||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
* @author vlan
|
||||
*/
|
||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
|
||||
public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun executeForAllCarets(
|
||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
* @author vlan
|
||||
*/
|
||||
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
|
||||
public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun executeForAllCarets(
|
||||
|
@@ -36,6 +36,18 @@ internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELET
|
||||
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
|
||||
override val type: Command.Type = Command.Type.MOTION
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments
|
||||
): Boolean {
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||
return super.execute(editor, context, cmd, operatorArguments)
|
||||
}
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
|
||||
@@ -48,6 +60,18 @@ internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
|
||||
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
|
||||
override val type: Command.Type = Command.Type.MOTION
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments
|
||||
): Boolean {
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||
return super.execute(editor, context, cmd, operatorArguments)
|
||||
}
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
|
||||
@@ -55,7 +79,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||
injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
|
||||
injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@@ -9,21 +9,18 @@
|
||||
package com.maddyhome.idea.vim.command
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Additional class
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
public class CommandState(private val machine: VimStateMachine) {
|
||||
@Deprecated("Use `injector.vimState`")
|
||||
class CommandState(private val machine: VimStateMachine) {
|
||||
|
||||
public val isOperatorPending: Boolean
|
||||
get() = machine.isOperatorPending(machine.mode)
|
||||
|
||||
public val mode: Mode
|
||||
val mode: Mode
|
||||
get() {
|
||||
val myMode = machine.mode
|
||||
return when (myMode) {
|
||||
@@ -37,13 +34,23 @@ public class CommandState(private val machine: VimStateMachine) {
|
||||
}
|
||||
}
|
||||
|
||||
public val commandBuilder: CommandBuilder
|
||||
get() = machine.commandBuilder
|
||||
@Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
|
||||
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
|
||||
"com.maddyhome.idea.vim.KeyHandler"
|
||||
)
|
||||
)
|
||||
val commandBuilder: CommandBuilder
|
||||
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
|
||||
|
||||
public val mappingState: MappingState
|
||||
get() = machine.mappingState
|
||||
@Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
|
||||
"KeyHandler.getInstance().keyHandlerState.mappingState",
|
||||
"com.maddyhome.idea.vim.KeyHandler"
|
||||
)
|
||||
)
|
||||
val mappingState: MappingState
|
||||
get() = KeyHandler.getInstance().keyHandlerState.mappingState
|
||||
|
||||
public enum class Mode {
|
||||
enum class Mode {
|
||||
// Basic modes
|
||||
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
|
||||
|
||||
@@ -51,22 +58,15 @@ public class CommandState(private val machine: VimStateMachine) {
|
||||
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
|
||||
}
|
||||
|
||||
public enum class SubMode {
|
||||
enum class SubMode {
|
||||
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||
}
|
||||
|
||||
public companion object {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
public fun getInstance(editor: Editor): CommandState {
|
||||
return CommandState(editor.vim.vimStateMachine)
|
||||
@Deprecated("Use `injector.vimState`")
|
||||
fun getInstance(editor: Editor): CommandState {
|
||||
return CommandState(injector.vimState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val CommandState.SubMode.engine: SelectionType
|
||||
get() = when (this) {
|
||||
CommandState.SubMode.NONE -> error("Unexpected value")
|
||||
CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
|
||||
CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
|
||||
CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
|
||||
}
|
||||
|
@@ -12,18 +12,18 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
|
||||
public class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
||||
public fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
||||
class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
||||
fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
||||
|
||||
public companion object {
|
||||
public fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
||||
companion object {
|
||||
fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
||||
// logical position "expands" tabs
|
||||
val logicalPosition = editor.offsetToLogicalPosition(offset)
|
||||
val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
|
||||
return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
|
||||
}
|
||||
|
||||
public fun atCaret(editor: Editor): CharacterPosition {
|
||||
fun atCaret(editor: Editor): CharacterPosition {
|
||||
return fromOffset(editor, editor.caretModel.offset)
|
||||
}
|
||||
}
|
||||
|
@@ -12,17 +12,17 @@ import com.intellij.application.options.CodeStyle
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
||||
import com.maddyhome.idea.vim.api.VimIndentConfig
|
||||
|
||||
internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
||||
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
|
||||
private val indentSize = indentOptions.INDENT_SIZE
|
||||
private val tabSize = indentOptions.TAB_SIZE
|
||||
private val isUseTabs = indentOptions.USE_TAB_CHARACTER
|
||||
|
||||
fun getTotalIndent(count: Int): Int = indentSize * count
|
||||
override fun getIndentSize(depth: Int): Int = indentSize * depth
|
||||
override fun createIndentByDepth(depth: Int): String = createIndentBySize(getIndentSize(depth))
|
||||
|
||||
fun createIndentByCount(count: Int): String = createIndentBySize(getTotalIndent(count))
|
||||
|
||||
fun createIndentBySize(size: Int): String {
|
||||
override fun createIndentBySize(size: Int): String {
|
||||
val tabCount: Int
|
||||
val spaceCount: Int
|
||||
if (isUseTabs) {
|
||||
|
@@ -18,6 +18,8 @@ import kotlin.reflect.KProperty
|
||||
internal class VimState {
|
||||
var isIdeaJoinNotified by StateProperty("idea-join")
|
||||
var isIdeaPutNotified by StateProperty("idea-put")
|
||||
var isNewUndoNotified by StateProperty("idea-undo")
|
||||
var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
|
||||
|
||||
fun readData(element: Element) {
|
||||
val notifications = element.getChild("notifications")
|
||||
|
@@ -9,62 +9,142 @@ package com.maddyhome.idea.vim.ex
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.api.VimExOutputPanel
|
||||
import com.maddyhome.idea.vim.api.VimOutputPanelBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.helper.vimExOutput
|
||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
|
||||
public class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
|
||||
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() {
|
||||
private var isActiveInTestMode = false
|
||||
|
||||
override val isActive: Boolean
|
||||
val editor get() = myEditor.get()
|
||||
|
||||
val isActive: Boolean
|
||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.isPanelActive(myEditor)
|
||||
editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
|
||||
} else {
|
||||
isActiveInTestMode
|
||||
}
|
||||
|
||||
override var text: String? = null
|
||||
override fun addText(text: String, isNewLine: Boolean) {
|
||||
if (this.text.isNotEmpty() && isNewLine) this.text += "\n$text" else this.text += text
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
if (editor == null) return
|
||||
val currentPanel = injector.outputPanel.getCurrentOutputPanel()
|
||||
if (currentPanel != null && currentPanel != this) currentPanel.close()
|
||||
|
||||
editor!!.vimExOutput = this
|
||||
val exOutputPanel = ExOutputPanel.getInstance(editor!!)
|
||||
if (!exOutputPanel.myActive) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
isActiveInTestMode = true
|
||||
} else {
|
||||
exOutputPanel.activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun scrollPage() {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.scrollPage()
|
||||
}
|
||||
|
||||
override fun scrollHalfPage() {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.scrollHalfPage()
|
||||
}
|
||||
|
||||
override fun scrollLine() {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.scrollLine()
|
||||
}
|
||||
|
||||
override var text: String = ""
|
||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).text
|
||||
editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
|
||||
} else {
|
||||
// ExOutputPanel always returns a non-null string
|
||||
field
|
||||
}
|
||||
set(value) {
|
||||
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
|
||||
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
|
||||
val newValue = value.removeSuffix("\n")
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).setText(value ?: "")
|
||||
editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
|
||||
} else {
|
||||
field = value
|
||||
isActiveInTestMode = !value.isNullOrEmpty()
|
||||
field = newValue
|
||||
isActiveInTestMode = newValue.isNotEmpty()
|
||||
}
|
||||
}
|
||||
override var label: String
|
||||
get() {
|
||||
val notNullEditor = editor ?: return ""
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return ""
|
||||
return panel.myLabel.text
|
||||
}
|
||||
set(value) {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.myLabel.text = value
|
||||
}
|
||||
|
||||
override fun output(text: String) {
|
||||
fun output(text: String) {
|
||||
this.text = text
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
text = null
|
||||
fun clear() {
|
||||
text = ""
|
||||
}
|
||||
|
||||
override val atEnd: Boolean
|
||||
get() {
|
||||
val notNullEditor = editor ?: return false
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return false
|
||||
return panel.isAtEnd()
|
||||
}
|
||||
|
||||
override fun onBadKey() {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.onBadKey()
|
||||
}
|
||||
|
||||
override fun close(key: KeyStroke?) {
|
||||
val notNullEditor = editor ?: return
|
||||
val panel = ExOutputPanel.getNullablePanel(notNullEditor) ?: return
|
||||
panel.close(key)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).close()
|
||||
editor?.let { ExOutputPanel.getInstance(it).close() }
|
||||
}
|
||||
else {
|
||||
isActiveInTestMode = false
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
public fun getInstance(editor: Editor): ExOutputModel {
|
||||
fun getInstance(editor: Editor): ExOutputModel {
|
||||
var model = editor.vimExOutput
|
||||
if (model == null) {
|
||||
model = ExOutputModel(editor)
|
||||
model = ExOutputModel(WeakReference(editor))
|
||||
editor.vimExOutput = model
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun tryGetInstance(editor: Editor) = editor.vimExOutput
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ package com.maddyhome.idea.vim.extension
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
@@ -25,13 +24,14 @@ import com.maddyhome.idea.vim.common.CommandAlias
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||
import com.maddyhome.idea.vim.helper.inRepeatMode
|
||||
import com.maddyhome.idea.vim.helper.noneOfEnum
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
||||
@@ -51,13 +51,13 @@ import javax.swing.KeyStroke
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public object VimExtensionFacade {
|
||||
object VimExtensionFacade {
|
||||
|
||||
private val LOG = logger<VimExtensionFacade>()
|
||||
|
||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||
@JvmStatic
|
||||
public fun putExtensionHandlerMapping(
|
||||
fun putExtensionHandlerMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -68,13 +68,15 @@ public object VimExtensionFacade {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Additional method
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||
@JvmStatic
|
||||
public fun putExtensionHandlerMapping(
|
||||
@Deprecated(
|
||||
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||
ReplaceWith(
|
||||
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||
"com.maddyhome.idea.vim.VimPlugin"
|
||||
)
|
||||
)
|
||||
fun putExtensionHandlerMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -86,7 +88,7 @@ public object VimExtensionFacade {
|
||||
|
||||
/** The 'map' command for mapping keys to other keys. */
|
||||
@JvmStatic
|
||||
public fun putKeyMapping(
|
||||
fun putKeyMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -98,7 +100,7 @@ public object VimExtensionFacade {
|
||||
|
||||
/** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
|
||||
@JvmStatic
|
||||
public fun putKeyMappingIfMissing(
|
||||
fun putKeyMappingIfMissing(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -112,7 +114,7 @@ public object VimExtensionFacade {
|
||||
/**
|
||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||
*/
|
||||
public fun addCommand(
|
||||
fun addCommand(
|
||||
name: String,
|
||||
handler: CommandAliasHandler,
|
||||
) {
|
||||
@@ -123,7 +125,7 @@ public object VimExtensionFacade {
|
||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun addCommand(
|
||||
fun addCommand(
|
||||
name: String,
|
||||
minimumNumberOfArguments: Int,
|
||||
maximumNumberOfArguments: Int,
|
||||
@@ -141,7 +143,7 @@ public object VimExtensionFacade {
|
||||
* leaves the editor in the insert mode if it's been activated.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
||||
@@ -149,8 +151,8 @@ public object VimExtensionFacade {
|
||||
|
||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||
@JvmStatic
|
||||
public fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
|
||||
fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||
if (editor.vim.inRepeatMode) {
|
||||
val input = Extension.consumeKeystroke()
|
||||
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
||||
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
|
||||
@@ -181,43 +183,43 @@ public object VimExtensionFacade {
|
||||
|
||||
/** Returns a string typed in the input box similar to 'input()'. */
|
||||
@JvmStatic
|
||||
public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||
return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||
return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||
}
|
||||
|
||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||
@JvmStatic
|
||||
public fun getRegister(register: Char): List<KeyStroke>? {
|
||||
fun getRegister(register: Char): List<KeyStroke>? {
|
||||
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
||||
return reg.keys
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||
val reg = caret.registerStorage.getRegister(register) ?: return null
|
||||
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||
val reg = injector.registerGroup.getRegister(register) ?: return null
|
||||
return reg.keys
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||
injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun exportScriptFunction(
|
||||
fun exportScriptFunction(
|
||||
scope: Scope?,
|
||||
name: String,
|
||||
args: List<String>,
|
||||
@@ -253,7 +255,7 @@ public object VimExtensionFacade {
|
||||
}
|
||||
}
|
||||
|
||||
public fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
||||
editor, context, args ->
|
||||
|
||||
@@ -274,6 +276,6 @@ public fun VimExtensionFacade.exportOperatorFunction(name: String, function: Ope
|
||||
}
|
||||
}
|
||||
|
||||
public fun interface ScriptFunction {
|
||||
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||
}
|
||||
fun interface ScriptFunction {
|
||||
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||
}
|
||||
|
@@ -19,12 +19,12 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
* COMPATIBILITY-LAYER: Created a class, renamed original class
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
public interface VimExtensionHandler : ExtensionHandler {
|
||||
interface VimExtensionHandler : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
execute(editor.ij, context.ij)
|
||||
}
|
||||
|
||||
public fun execute(editor: Editor, context: DataContext)
|
||||
fun execute(editor: Editor, context: DataContext)
|
||||
|
||||
public abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
||||
abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.argtextobj;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.command.*;
|
||||
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -244,19 +245,18 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
|
||||
IjVimEditor vimEditor = (IjVimEditor) editor;
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
|
||||
@@ -265,7 +265,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
@@ -45,7 +46,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
@@ -64,7 +64,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
selectionType: SelectionType,
|
||||
resetCaret: Boolean = true,
|
||||
): Boolean {
|
||||
val mode = editor.vimStateMachine.mode
|
||||
val mode = editor.mode
|
||||
if (mode !is Mode.VISUAL) {
|
||||
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
||||
}
|
||||
@@ -79,11 +79,12 @@ internal class CommentaryExtension : VimExtension {
|
||||
|
||||
val project = editor.ij.project!!
|
||||
val callback = { afterCommenting(mode, editor, resetCaret, range) }
|
||||
actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
|
||||
actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeActionWithCallbackOnSuccess(
|
||||
editor: VimEditor,
|
||||
action: String,
|
||||
project: Project,
|
||||
context: ExecutionContext,
|
||||
@@ -92,7 +93,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
val res = Ref.create<Boolean>(false)
|
||||
AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
|
||||
action,
|
||||
{ res.set(injector.actionExecutor.executeAction(action, context)) },
|
||||
{ res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) },
|
||||
{ if (res.get()) callback() })
|
||||
return res.get()
|
||||
}
|
||||
@@ -183,10 +184,10 @@ internal class CommentaryExtension : VimExtension {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val commandState = editor.vimStateMachine
|
||||
|
||||
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
|
||||
commandState.commandBuilder.completeCommandPart(Argument(command))
|
||||
|
||||
val keyState = KeyHandler.getInstance().keyHandlerState
|
||||
keyState.commandBuilder.completeCommandPart(Argument(command))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,13 +23,15 @@ import com.intellij.util.Alarm
|
||||
import com.intellij.util.Alarm.ThreadToUse
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.VimYankListener
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
||||
import com.maddyhome.idea.vim.listener.VimYankListener
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import org.jetbrains.annotations.NonNls
|
||||
@@ -75,7 +77,7 @@ internal class HighlightColorResetter : LafManagerListener {
|
||||
*
|
||||
* When a new text is yanked or user starts editing, the old highlighting would be deleted.
|
||||
*/
|
||||
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
|
||||
internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeListener {
|
||||
private val highlightHandler = HighlightHandler()
|
||||
private var initialised = false
|
||||
|
||||
@@ -83,8 +85,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
||||
|
||||
override fun init() {
|
||||
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
|
||||
VimPlugin.getYank().addListener(this)
|
||||
VimPlugin.getChange().addInsertListener(this)
|
||||
injector.listenersNotifier.modeChangeListeners.add(this)
|
||||
injector.listenersNotifier.yankListeners.add(this)
|
||||
|
||||
// Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
|
||||
// IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
|
||||
@@ -105,8 +107,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
||||
|
||||
override fun dispose() {
|
||||
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
|
||||
VimPlugin.getYank().removeListener(this)
|
||||
VimPlugin.getChange().removeInsertListener(this)
|
||||
injector.listenersNotifier.modeChangeListeners.remove(this)
|
||||
injector.listenersNotifier.yankListeners.remove(this)
|
||||
|
||||
highlightHandler.clearYankHighlighters()
|
||||
initialised = false
|
||||
@@ -117,7 +119,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
||||
highlightHandler.highlightYankRange(editor.ij, range)
|
||||
}
|
||||
|
||||
override fun insertModeStarted(editor: Editor) {
|
||||
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
|
||||
if (editor.mode !is Mode.INSERT) return
|
||||
ensureInitialised()
|
||||
highlightHandler.clearYankHighlighters()
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
@@ -40,7 +41,6 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
@@ -91,22 +91,23 @@ internal class Matchit : VimExtension {
|
||||
private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val commandState = editor.vimStateMachine
|
||||
val count = commandState.commandBuilder.count
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
val keyState = keyHandler.keyHandlerState
|
||||
val count = keyState.commandBuilder.count
|
||||
|
||||
// Reset the command count so it doesn't transfer onto subsequent commands.
|
||||
editor.vimStateMachine.commandBuilder.resetCount()
|
||||
keyState.commandBuilder.resetCount()
|
||||
|
||||
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
||||
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
||||
val isInOpPending = commandState.isOperatorPending(editor.mode)
|
||||
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState)
|
||||
|
||||
if (isInOpPending) {
|
||||
val matchitAction = MatchitAction()
|
||||
matchitAction.reverse = reverse
|
||||
matchitAction.isInOpPending = true
|
||||
|
||||
commandState.commandBuilder.completeCommandPart(
|
||||
keyState.commandBuilder.completeCommandPart(
|
||||
Argument(
|
||||
Command(
|
||||
count,
|
||||
|
@@ -30,11 +30,11 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchOptions
|
||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
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.updateCaretsVisualAttributes
|
||||
import com.maddyhome.idea.vim.helper.userData
|
||||
@@ -235,7 +235,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
val text = if (editor.inVisualMode) {
|
||||
primaryCaret.selectedText ?: return
|
||||
} else {
|
||||
val range = SearchHelper.findWordUnderCursor(editor, primaryCaret) ?: return
|
||||
val range = findWordUnderCursor(editor, primaryCaret) ?: return
|
||||
if (range.startOffset > primaryCaret.offset) return
|
||||
IjVimEditor(editor).getText(range)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
}
|
||||
|
||||
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
||||
val range = SearchHelper.findWordUnderCursor(editor, caret) ?: return null
|
||||
val range = findWordUnderCursor(editor, caret) ?: return null
|
||||
if (range.startOffset > caret.offset) return null
|
||||
|
||||
enterVisualMode(editor.vim)
|
||||
@@ -327,6 +327,6 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
|
||||
private fun makePattern(text: String, whole: Boolean): String {
|
||||
// Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
|
||||
return "\\V\\C" + SearchHelper.makeSearchPattern(text, whole)
|
||||
return "\\V\\C" + if (whole) "\\<$text\\>" else text
|
||||
}
|
||||
}
|
||||
|
@@ -478,6 +478,12 @@ internal class NerdTree : VimExtension {
|
||||
NerdAction.ToIj("SynchronizeCurrentFile"),
|
||||
)
|
||||
registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
|
||||
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
|
||||
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
|
||||
registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
|
||||
registerCommand("NERDTreeMapCopy", "y", NerdAction.ToIj("\$Copy"))
|
||||
registerCommand("NERDTreeMapPaste", "v", NerdAction.ToIj("\$Paste"))
|
||||
registerCommand("NERDTreeMapRename", "<C-r>", NerdAction.ToIj("RenameElement"))
|
||||
registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
|
||||
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
||||
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
||||
|
@@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.extension.replacewithregister
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
@@ -28,7 +29,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
@@ -144,7 +144,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||
val registerGroup = injector.registerGroup
|
||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||
val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
@@ -166,17 +166,18 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
|
||||
putToLine = -1,
|
||||
)
|
||||
val vimEditor = editor.vim
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
vimEditor,
|
||||
context.vim,
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.group.findBlockRange
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
@@ -50,6 +51,7 @@ import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
||||
|
||||
/**
|
||||
* Port of vim-surround.
|
||||
@@ -115,6 +117,9 @@ internal class VimSurroundExtension : VimExtension {
|
||||
// it.moveToOffset(lineStartOffset)
|
||||
}
|
||||
// Jump back to start
|
||||
if (editor.mode !is Mode.NORMAL) {
|
||||
editor.mode = Mode.NORMAL()
|
||||
}
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||
}
|
||||
|
||||
@@ -136,7 +141,7 @@ internal class VimSurroundExtension : VimExtension {
|
||||
}
|
||||
runWriteAction {
|
||||
// Leave visual mode
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
||||
editor.exitVisualMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,8 +311,10 @@ internal class VimSurroundExtension : VimExtension {
|
||||
|
||||
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
||||
val editor = caret.editor
|
||||
val ijEditor = editor.ij
|
||||
return when (ijEditor.vim.mode) {
|
||||
if (editor.mode is Mode.CMD_LINE) {
|
||||
editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
|
||||
}
|
||||
return when (editor.mode) {
|
||||
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
|
||||
is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
|
||||
else -> null
|
||||
@@ -350,6 +357,9 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
|
||||
|
||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
||||
val tagInput = inputString(editor, context, "<", '>')
|
||||
if (editor.vim.mode is Mode.CMD_LINE) {
|
||||
editor.vim.mode = editor.vim.mode.returnTo()
|
||||
}
|
||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||
return if (matcher.find()) {
|
||||
val tagName = matcher.group(1)
|
||||
@@ -366,6 +376,9 @@ private fun inputFunctionName(
|
||||
withInternalSpaces: Boolean,
|
||||
): Pair<String, String>? {
|
||||
val functionNameInput = inputString(editor, context, "function: ", null)
|
||||
if (editor.vim.mode is Mode.CMD_LINE) {
|
||||
editor.vim.mode = editor.vim.mode.returnTo()
|
||||
}
|
||||
if (functionNameInput.isEmpty()) return null
|
||||
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.textobjentire;
|
||||
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -133,17 +134,18 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||
@@ -153,7 +155,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION,
|
||||
EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.textobjindent;
|
||||
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
@@ -24,7 +25,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -263,17 +264,18 @@ public class VimIndentObject implements VimExtension {
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
IjVimEditor vimEditor = (IjVimEditor)editor;
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||
@@ -283,7 +285,7 @@ public class VimIndentObject implements VimExtension {
|
||||
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION,
|
||||
EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
|
@@ -15,76 +15,35 @@ import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.actions.EnterAction
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent
|
||||
import com.intellij.openapi.editor.event.EditorMouseListener
|
||||
import com.intellij.openapi.editor.impl.TextRangeInterval
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import com.intellij.psi.util.PsiUtilBase
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.maddyhome.idea.vim.EventFacade
|
||||
import com.maddyhome.idea.vim.api.BufferPosition
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
||||
import com.maddyhome.idea.vim.api.getText
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.lineLength
|
||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||
import com.maddyhome.idea.vim.api.options
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.IndentConfig.Companion.create
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||
import com.maddyhome.idea.vim.group.MotionGroup.Companion.getMotionRange2
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
||||
import com.maddyhome.idea.vim.handler.Motion
|
||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||
import com.maddyhome.idea.vim.handler.commandContinuation
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper.changeCase
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.NumberType
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||
import com.maddyhome.idea.vim.helper.inInsertMode
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.regexp.VimRegex
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.math.BigInteger
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* Provides all the insert/replace related functionality
|
||||
*/
|
||||
public class ChangeGroup : VimChangeGroupBase() {
|
||||
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
|
||||
class ChangeGroup : VimChangeGroupBase() {
|
||||
private val listener: EditorMouseListener = object : EditorMouseListener {
|
||||
override fun mouseClicked(event: EditorMouseEvent) {
|
||||
val editor = event.editor
|
||||
@@ -94,7 +53,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
public fun editorCreated(editor: Editor?, disposable: Disposable) {
|
||||
fun editorCreated(editor: Editor?, disposable: Disposable) {
|
||||
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
|
||||
}
|
||||
|
||||
@@ -102,6 +61,9 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val ijContext = context.ij
|
||||
val doc = vimEditor.editor.document
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
editor.project, {
|
||||
ApplicationManager.getApplication()
|
||||
@@ -141,163 +103,6 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDeleteRangeAndType2(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
argument: Argument,
|
||||
isChange: Boolean,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Pair<TextRange, SelectionType>? {
|
||||
val range = getMotionRange2(
|
||||
(editor as IjVimEditor).editor,
|
||||
(caret as IjVimCaret).caret,
|
||||
(context as IjEditorExecutionContext).context,
|
||||
argument,
|
||||
operatorArguments
|
||||
)
|
||||
?: return null
|
||||
|
||||
// Delete motion commands that are not linewise become linewise if all the following are true:
|
||||
// 1) The range is across multiple lines
|
||||
// 2) There is only whitespace before the start of the range
|
||||
// 3) There is only whitespace after the end of the range
|
||||
var type: SelectionType
|
||||
type = if (argument.motion.isLinewiseMotion()) {
|
||||
SelectionType.LINE_WISE
|
||||
} else {
|
||||
SelectionType.CHARACTER_WISE
|
||||
}
|
||||
val motion = argument.motion
|
||||
if (!isChange && !motion.isLinewiseMotion()) {
|
||||
val start = editor.offsetToBufferPosition(range.startOffset)
|
||||
val end = editor.offsetToBufferPosition(range.endOffset)
|
||||
if (start.line != end.line) {
|
||||
val offset1 = range.startOffset
|
||||
if (!editor.anyNonWhitespace(offset1, -1)) {
|
||||
val offset = range.endOffset
|
||||
if (!editor.anyNonWhitespace(offset, 1)) {
|
||||
type = SelectionType.LINE_WISE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair(range, type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the case of count characters
|
||||
*
|
||||
* @param editor The editor to change
|
||||
* @param caret The caret on which the operation is performed
|
||||
* @param count The number of characters to change
|
||||
* @return true if able to change count characters
|
||||
*/
|
||||
override fun changeCaseToggleCharacter(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
|
||||
val allowWrap = injector.options(editor).whichwrap.contains("~")
|
||||
var motion = injector.motion.getHorizontalMotion(editor, caret, count, true, allowWrap)
|
||||
if (motion is Motion.Error) return false
|
||||
changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||
motion = injector.motion.getHorizontalMotion(
|
||||
editor,
|
||||
caret,
|
||||
count,
|
||||
false,
|
||||
allowWrap
|
||||
) // same but without allow end because we can change till end, but can't move caret there
|
||||
if (motion is AbsoluteOffset) {
|
||||
caret.moveToOffset(editor.normalizeOffset(motion.offset, false))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun blockInsert(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
append: Boolean,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
val lines = getLinesCountInVisualBlock(editor, range)
|
||||
val startPosition = editor.offsetToBufferPosition(range.startOffset)
|
||||
val mode = operatorArguments.mode
|
||||
val visualBlockMode = mode is VISUAL && mode.selectionType === SelectionType.BLOCK_WISE
|
||||
for (caret in editor.carets()) {
|
||||
val line = startPosition.line
|
||||
var column = startPosition.column
|
||||
if (!visualBlockMode) {
|
||||
column = 0
|
||||
} else if (append) {
|
||||
column += range.maxLength
|
||||
if (caret.vimLastColumn == VimMotionGroupBase.LAST_COLUMN) {
|
||||
column = VimMotionGroupBase.LAST_COLUMN
|
||||
}
|
||||
}
|
||||
val lineLength = editor.lineLength(line)
|
||||
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
|
||||
val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
|
||||
val offset = editor.getLineEndOffset(line)
|
||||
insertText(editor, caret, offset, pad)
|
||||
}
|
||||
if (visualBlockMode || !append) {
|
||||
(caret as IjVimCaret).caret.moveToInlayAwareLogicalPosition(LogicalPosition(line, column))
|
||||
}
|
||||
if (visualBlockMode) {
|
||||
setInsertRepeat(lines, column, append)
|
||||
}
|
||||
}
|
||||
if (visualBlockMode || !append) {
|
||||
insertBeforeCursor(editor, context)
|
||||
} else {
|
||||
insertAfterCursor(editor, context)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the case of all the characters in the range
|
||||
*
|
||||
* @param editor The editor to change
|
||||
* @param caret The caret to be moved
|
||||
* @param range The range to change
|
||||
* @param type The case change type (TOGGLE, UPPER, LOWER)
|
||||
* @return true if able to delete the text, false if not
|
||||
*/
|
||||
override fun changeCaseRange(editor: VimEditor, caret: VimCaret, range: TextRange, type: Char): Boolean {
|
||||
val starts = range.startOffsets
|
||||
val ends = range.endOffsets
|
||||
for (i in ends.indices.reversed()) {
|
||||
changeCase(editor, caret, starts[i], ends[i], type)
|
||||
}
|
||||
caret.moveToOffset(range.startOffset)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* This performs the actual case change.
|
||||
*
|
||||
* @param editor The editor to change
|
||||
* @param start The start offset to change
|
||||
* @param end The end offset to change
|
||||
* @param type The type of change (TOGGLE, UPPER, LOWER)
|
||||
*/
|
||||
private fun changeCase(editor: VimEditor, caret: VimCaret, start: Int, end: Int, type: Char) {
|
||||
var start = start
|
||||
var end = end
|
||||
if (start > end) {
|
||||
val t = end
|
||||
end = start
|
||||
start = t
|
||||
}
|
||||
end = editor.normalizeOffset(end, true)
|
||||
val chars = editor.text()
|
||||
val sb = StringBuilder()
|
||||
for (i in start until end) {
|
||||
sb.append(changeCase(chars[i], type))
|
||||
}
|
||||
replaceText(editor, caret, start, end, sb.toString())
|
||||
}
|
||||
|
||||
private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
|
||||
if (caret != editor.primaryCaret()) {
|
||||
(editor as IjVimEditor).editor.caretModel.addCaret(
|
||||
@@ -306,88 +111,13 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the case of all the character moved over by the motion argument.
|
||||
*
|
||||
* @param editor The editor to change
|
||||
* @param caret The caret on which motion pretends to be performed
|
||||
* @param context The data context
|
||||
* @param type The case change type (TOGGLE, UPPER, LOWER)
|
||||
* @param argument The motion command
|
||||
* @return true if able to delete the text, false if not
|
||||
*/
|
||||
override fun changeCaseMotion(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext?,
|
||||
type: Char,
|
||||
argument: Argument,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
val range = injector.motion.getMotionRange(
|
||||
editor, caret, context!!, argument,
|
||||
operatorArguments
|
||||
)
|
||||
return range != null && changeCaseRange(editor, caret, range, type)
|
||||
}
|
||||
|
||||
override fun reformatCodeMotion(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
argument: Argument,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
val range = injector.motion.getMotionRange(
|
||||
editor, caret, context, argument,
|
||||
operatorArguments
|
||||
)
|
||||
return range != null && reformatCodeRange(editor, caret, range)
|
||||
}
|
||||
|
||||
override fun reformatCodeSelection(editor: VimEditor, caret: VimCaret, range: VimSelection) {
|
||||
val textRange = range.toVimTextRange(true)
|
||||
reformatCodeRange(editor, caret, textRange)
|
||||
}
|
||||
|
||||
private fun reformatCodeRange(editor: VimEditor, caret: VimCaret, range: TextRange): Boolean {
|
||||
val starts = range.startOffsets
|
||||
val ends = range.endOffsets
|
||||
val firstLine = editor.offsetToBufferPosition(range.startOffset).line
|
||||
for (i in ends.indices.reversed()) {
|
||||
val startOffset = editor.getLineStartForOffset(starts[i])
|
||||
val offset = ends[i] - if (startOffset == ends[i]) 0 else 1
|
||||
val endOffset = editor.getLineEndForOffset(offset)
|
||||
reformatCode(editor, startOffset, endOffset)
|
||||
}
|
||||
val newOffset = injector.motion.moveCaretToLineStartSkipLeading(editor, firstLine)
|
||||
caret.moveToOffset(newOffset)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun reformatCode(editor: VimEditor, start: Int, end: Int) {
|
||||
override fun reformatCode(editor: VimEditor, start: Int, end: Int) {
|
||||
val project = (editor as IjVimEditor).editor.project ?: return
|
||||
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
||||
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
||||
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
||||
}
|
||||
|
||||
override fun autoIndentMotion(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
argument: Argument,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
|
||||
if (range != null) {
|
||||
autoIndentRange(
|
||||
editor, caret, context,
|
||||
TextRange(range.startOffset, range.endOffsetInclusive)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun autoIndentRange(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
@@ -442,362 +172,14 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun indentLines(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
lines: Int,
|
||||
dir: Int,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
val start = caret.offset
|
||||
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
|
||||
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
|
||||
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
||||
fun addInsertListener(listener: VimInsertListener) {
|
||||
injector.listenersNotifier.modeChangeListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun indentMotion(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
argument: Argument,
|
||||
dir: Int,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
val range = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
|
||||
if (range != null) {
|
||||
indentRange(editor, caret, context, range, 1, dir, operatorArguments)
|
||||
}
|
||||
}
|
||||
|
||||
override fun indentRange(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
count: Int,
|
||||
dir: Int,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("count=$count")
|
||||
}
|
||||
|
||||
// Remember the current caret column
|
||||
val intendedColumn = caret.vimLastColumn
|
||||
val indentConfig = create((editor as IjVimEditor).editor)
|
||||
val sline = editor.offsetToBufferPosition(range.startOffset).line
|
||||
val endLogicalPosition = editor.offsetToBufferPosition(range.endOffset)
|
||||
val eline = if (endLogicalPosition.column == 0) max((endLogicalPosition.line - 1).toDouble(), 0.0)
|
||||
.toInt() else endLogicalPosition.line
|
||||
if (range.isMultiple) {
|
||||
val from = editor.offsetToBufferPosition(range.startOffset).column
|
||||
if (dir == 1) {
|
||||
// Right shift blockwise selection
|
||||
val indent = indentConfig.createIndentByCount(count)
|
||||
for (l in sline..eline) {
|
||||
val len = editor.lineLength(l)
|
||||
if (len > from) {
|
||||
val spos = BufferPosition(l, from, false)
|
||||
insertText(editor, caret, spos, indent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Left shift blockwise selection
|
||||
val chars = editor.text()
|
||||
for (l in sline..eline) {
|
||||
val len = editor.lineLength(l)
|
||||
if (len > from) {
|
||||
val spos = BufferPosition(l, from, false)
|
||||
val epos = BufferPosition(l, from + indentConfig.getTotalIndent(count) - 1, false)
|
||||
val wsoff = editor.bufferPositionToOffset(spos)
|
||||
val weoff = editor.bufferPositionToOffset(epos)
|
||||
var pos: Int
|
||||
pos = wsoff
|
||||
while (pos <= weoff) {
|
||||
if (charType(editor, chars[pos], false) !== CharacterHelper.CharacterType.WHITESPACE) {
|
||||
break
|
||||
}
|
||||
pos++
|
||||
}
|
||||
if (pos > wsoff) {
|
||||
deleteText(editor, TextRange(wsoff, pos), null, caret, operatorArguments, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Shift non-blockwise selection
|
||||
for (l in sline..eline) {
|
||||
val soff = editor.getLineStartOffset(l)
|
||||
val eoff = editor.getLineEndOffset(l, true)
|
||||
val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
|
||||
val col = editor.offsetToBufferPosition(woff).column
|
||||
val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
|
||||
.toInt()
|
||||
if (col > 0 || soff != eoff) {
|
||||
val indent = indentConfig.createIndentBySize(limit)
|
||||
replaceText(editor, caret, soff, woff, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!editor.editor.inInsertMode) {
|
||||
if (!range.isMultiple) {
|
||||
// The caret has moved, so reset the intended column before trying to get the expected offset
|
||||
val newCaret = caret.setVimLastColumnAndGetCaret(intendedColumn)
|
||||
val offset = injector.motion.moveCaretToLineWithStartOfLineOption(editor, sline, caret)
|
||||
newCaret.moveToOffset(offset)
|
||||
} else {
|
||||
caret.moveToOffset(range.startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort range of text with a given comparator
|
||||
*
|
||||
* @param editor The editor to replace text in
|
||||
* @param range The range to sort
|
||||
* @param lineComparator The comparator to use to sort
|
||||
* @param sortOptions The option to sort the range
|
||||
* @return true if able to sort the text, false if not
|
||||
*/
|
||||
override fun sortRange(
|
||||
editor: VimEditor, caret: VimCaret, range: LineRange, lineComparator: Comparator<String>,
|
||||
sortOptions: SortOption,
|
||||
): Boolean {
|
||||
val startLine = range.startLine
|
||||
val endLine = range.endLine
|
||||
val count = range.size
|
||||
if (count < 2) {
|
||||
return false
|
||||
}
|
||||
val startOffset = editor.getLineStartOffset(startLine)
|
||||
val endOffset = editor.getLineEndOffset(endLine)
|
||||
|
||||
val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(startOffset, endOffset))
|
||||
val lines = selectedText.split("\n")
|
||||
val modifiedLines = sortOptions.pattern?.let {
|
||||
if (sortOptions.sortOnPattern) {
|
||||
extractPatternFromLines(editor, lines, startLine, it)
|
||||
} else {
|
||||
deletePatternFromLines(editor, lines, startLine, it)
|
||||
}
|
||||
} ?: lines
|
||||
val sortedLines = lines.zip(modifiedLines)
|
||||
.sortedWith { l1, l2 -> lineComparator.compare(l1.second, l2.second) }
|
||||
.map {it.first}
|
||||
.toMutableList()
|
||||
|
||||
if (sortOptions.unique) {
|
||||
val iterator = sortedLines.iterator()
|
||||
var previous: String? = null
|
||||
while (iterator.hasNext()) {
|
||||
val current = iterator.next()
|
||||
if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
previous = current
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sortedLines.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
replaceText(editor, caret, startOffset, endOffset, StringUtil.join(sortedLines, "\n"))
|
||||
return true
|
||||
}
|
||||
|
||||
private fun extractPatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
|
||||
val regex = VimRegex(pattern)
|
||||
return lines.mapIndexed { i: Int, line: String ->
|
||||
val result = regex.findInLine(editor, startLine + i, 0)
|
||||
when (result) {
|
||||
is VimMatchResult.Success -> result.value
|
||||
is VimMatchResult.Failure -> line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deletePatternFromLines(editor: VimEditor, lines: List<String>, startLine: Int, pattern: String): List<String> {
|
||||
val regex = VimRegex(pattern)
|
||||
return lines.mapIndexed { i: Int, line: String ->
|
||||
val result = regex.findInLine(editor, startLine + i, 0)
|
||||
when (result) {
|
||||
is VimMatchResult.Success -> line.substring(result.value.length, line.length)
|
||||
is VimMatchResult.Failure -> line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform increment and decrement for numbers in visual mode
|
||||
*
|
||||
*
|
||||
* Flag [avalanche] marks if increment (or decrement) should be performed in avalanche mode
|
||||
* (for v_g_Ctrl-A and v_g_Ctrl-X commands)
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
override fun changeNumberVisualMode(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
selectedRange: TextRange,
|
||||
count: Int,
|
||||
avalanche: Boolean,
|
||||
): Boolean {
|
||||
|
||||
val nf: List<String> = injector.options(editor).nrformats
|
||||
val alpha = nf.contains("alpha")
|
||||
val hex = nf.contains("hex")
|
||||
val octal = nf.contains("octal")
|
||||
val numberRanges = SearchHelper.findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
|
||||
val newNumbers: MutableList<String?> = ArrayList()
|
||||
for (i in numberRanges.indices) {
|
||||
val numberRange = numberRanges[i]
|
||||
val iCount = if (avalanche) (i + 1) * count else count
|
||||
val newNumber = changeNumberInRange(editor, numberRange, iCount, alpha, hex, octal)
|
||||
newNumbers.add(newNumber)
|
||||
}
|
||||
for (i in newNumbers.indices.reversed()) {
|
||||
// Replace text bottom up. In other direction ranges will be desynchronized after inc numbers like 99
|
||||
val (first) = numberRanges[i]
|
||||
val newNumber = newNumbers[i]
|
||||
replaceText(editor, caret, first.startOffset, first.endOffset, newNumber!!)
|
||||
}
|
||||
(caret as IjVimCaret).caret.moveToInlayAwareOffset(selectedRange.startOffset)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun changeNumber(editor: VimEditor, caret: VimCaret, count: Int): Boolean {
|
||||
val nf: List<String> = injector.options(editor).nrformats
|
||||
val alpha = nf.contains("alpha")
|
||||
val hex = nf.contains("hex")
|
||||
val octal = nf.contains("octal")
|
||||
val range =
|
||||
SearchHelper.findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
|
||||
if (range == null) {
|
||||
logger.debug("no number on line")
|
||||
return false
|
||||
}
|
||||
val newNumber = changeNumberInRange(editor, range, count, alpha, hex, octal)
|
||||
return if (newNumber == null) {
|
||||
false
|
||||
} else {
|
||||
replaceText(editor, caret, range.first.startOffset, range.first.endOffset, newNumber)
|
||||
caret.caret.moveToInlayAwareOffset(range.first.startOffset + newNumber.length - 1)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
strokes.clear()
|
||||
repeatCharsCount = 0
|
||||
if (lastStrokes != null) {
|
||||
lastStrokes!!.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveStrokes(newStrokes: String?) {
|
||||
val chars = newStrokes!!.toCharArray()
|
||||
strokes.add(chars)
|
||||
}
|
||||
|
||||
private fun changeNumberInRange(
|
||||
editor: VimEditor,
|
||||
range: Pair<TextRange, NumberType>,
|
||||
count: Int,
|
||||
alpha: Boolean,
|
||||
hex: Boolean,
|
||||
octal: Boolean,
|
||||
): String? {
|
||||
val text = editor.getText(range.first)
|
||||
val numberType = range.second
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("found range $range")
|
||||
logger.debug("text=$text")
|
||||
}
|
||||
var number = text
|
||||
if (text.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
var ch = text[0]
|
||||
if (hex && NumberType.HEX == numberType) {
|
||||
if (!text.lowercase(Locale.getDefault()).startsWith(HEX_START)) {
|
||||
throw RuntimeException("Hex number should start with 0x: $text")
|
||||
}
|
||||
for (i in text.length - 1 downTo 2) {
|
||||
val index = "abcdefABCDEF".indexOf(text[i])
|
||||
if (index >= 0) {
|
||||
lastLower = index < 6
|
||||
break
|
||||
}
|
||||
}
|
||||
var num = BigInteger(text.substring(2), 16)
|
||||
num = num.add(BigInteger.valueOf(count.toLong()))
|
||||
if (num.compareTo(BigInteger.ZERO) < 0) {
|
||||
num = BigInteger(MAX_HEX_INTEGER, 16).add(BigInteger.ONE).add(num)
|
||||
}
|
||||
number = num.toString(16)
|
||||
number = number.padStart(text.length - 2, '0')
|
||||
if (!lastLower) {
|
||||
number = number.uppercase(Locale.getDefault())
|
||||
}
|
||||
number = text.substring(0, 2) + number
|
||||
} else if (octal && NumberType.OCT == numberType && text.length > 1) {
|
||||
if (!text.startsWith("0")) throw RuntimeException("Oct number should start with 0: $text")
|
||||
var num = BigInteger(text, 8).add(BigInteger.valueOf(count.toLong()))
|
||||
if (num.compareTo(BigInteger.ZERO) < 0) {
|
||||
num = BigInteger("1777777777777777777777", 8).add(BigInteger.ONE).add(num)
|
||||
}
|
||||
number = num.toString(8)
|
||||
number = "0" + number.padStart(text.length - 1, '0')
|
||||
} else if (alpha && NumberType.ALPHA == numberType) {
|
||||
if (!Character.isLetter(ch)) throw RuntimeException("Not alpha number : $text")
|
||||
ch += count.toChar().code
|
||||
if (Character.isLetter(ch)) {
|
||||
number = ch.toString()
|
||||
}
|
||||
} else if (NumberType.DEC == numberType) {
|
||||
if (ch != '-' && !Character.isDigit(ch)) throw RuntimeException("Not dec number : $text")
|
||||
var pad = ch == '0'
|
||||
var len = text.length
|
||||
if (ch == '-' && text[1] == '0') {
|
||||
pad = true
|
||||
len--
|
||||
}
|
||||
var num = BigInteger(text)
|
||||
num = num.add(BigInteger.valueOf(count.toLong()))
|
||||
number = num.toString()
|
||||
if (!octal && pad) {
|
||||
var neg = false
|
||||
if (number[0] == '-') {
|
||||
neg = true
|
||||
number = number.substring(1)
|
||||
}
|
||||
number = number.padStart(len, '0')
|
||||
if (neg) {
|
||||
number = "-$number"
|
||||
}
|
||||
}
|
||||
}
|
||||
return number
|
||||
}
|
||||
|
||||
public fun addInsertListener(listener: VimInsertListener) {
|
||||
insertListeners.add(listener)
|
||||
}
|
||||
|
||||
public fun removeInsertListener(listener: VimInsertListener) {
|
||||
insertListeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun notifyListeners(editor: VimEditor) {
|
||||
insertListeners.forEach(Consumer { listener: VimInsertListener -> listener.insertModeStarted((editor as IjVimEditor).editor) })
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
override fun resetRepeat() {
|
||||
setInsertRepeat(0, 0, false)
|
||||
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
||||
fun removeInsertListener(listener: VimInsertListener) {
|
||||
injector.listenersNotifier.modeChangeListeners.remove(listener)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
@@ -11,11 +11,14 @@ package com.maddyhome.idea.vim.group;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.api.VimDigraphGroupBase;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.api.VimOutputPanel;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
|
||||
public class DigraphGroup extends VimDigraphGroupBase {
|
||||
|
||||
public void showDigraphs(@NotNull VimEditor editor) {
|
||||
@@ -71,7 +74,9 @@ public class DigraphGroup extends VimDigraphGroupBase {
|
||||
}
|
||||
}
|
||||
|
||||
ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString());
|
||||
VimOutputPanel output = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
|
||||
output.addText(res.toString(), true );
|
||||
output.show();
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
|
||||
|
@@ -8,9 +8,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
||||
import com.intellij.find.EditorSearchSession;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.client.ClientAppSession;
|
||||
import com.intellij.openapi.client.ClientKind;
|
||||
import com.intellij.openapi.client.ClientSessionsManager;
|
||||
@@ -20,13 +18,13 @@ import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.event.CaretEvent;
|
||||
import com.intellij.openapi.editor.event.CaretListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||
@@ -38,6 +36,8 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -45,6 +45,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.openapi.editor.EditorSettings.*;
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
||||
|
||||
/**
|
||||
@@ -208,44 +209,14 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
|
||||
initLineNumbers(editor);
|
||||
|
||||
// We add Vim bindings to all opened editors, even read-only editors. We also add bindings to editors that are used
|
||||
// elsewhere in the IDE, rather than just for editing project files. This includes editors used as part of the UI,
|
||||
// such as the VCS commit message, or used as read-only viewers for text output, such as log files in run
|
||||
// configurations or the Git Console tab. And editors are used for interactive stdin/stdout for console-based run
|
||||
// configurations.
|
||||
// We want to provide an intuitive experience for working with these additional editors, so we automatically switch
|
||||
// to INSERT mode for interactive editors. Recognising these can be a bit tricky.
|
||||
// These additional interactive editors are not file-based, but must have a writable document. However, log output
|
||||
// documents are also writable (the IDE is writing new content as it becomes available) just not user-editable. So
|
||||
// we must also check that the editor is not in read-only "viewer" mode (this includes "rendered" mode, which is
|
||||
// read-only and also hides the caret).
|
||||
// Furthermore, the interactive stdin/stdout console output is hosted in a read-only editor, but it can still be
|
||||
// edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's `isViewer` property and
|
||||
// allows typing if the associated process (if any) is still running. We can get the editor's console view and check
|
||||
// this ourselves, but we have to wait until the editor has finished initialising before it's available in user
|
||||
// data.
|
||||
// Note that we need a similar check in `VimEditor.isWritable` to allow Escape to work to exit insert mode. We need
|
||||
// to know that a read-only editor that is hosting a console view with a running process can be treated as writable.
|
||||
Runnable switchToInsertMode = () -> {
|
||||
ExecutionContext context = injector.getExecutionContextManager().getEditorExecutionContext(new IjVimEditor(editor));
|
||||
VimPlugin.getChange().insertBeforeCursor(new IjVimEditor(editor), context);
|
||||
KeyHandler.getInstance().reset(new IjVimEditor(editor));
|
||||
};
|
||||
if (!editor.isViewer() &&
|
||||
!EditorHelper.isFileEditor(editor) &&
|
||||
editor.getDocument().isWritable() &&
|
||||
!CommandStateHelper.inInsertMode(editor)) {
|
||||
switchToInsertMode.run();
|
||||
// Listen for changes to the font size, so we can hide the ex text field/output panel
|
||||
if (editor instanceof EditorEx editorEx) {
|
||||
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
||||
}
|
||||
|
||||
if (injector.getApplication().isUnitTest()) {
|
||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
() -> {
|
||||
if (editor.isDisposed()) return;
|
||||
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
|
||||
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
|
||||
switchToInsertMode.run();
|
||||
}
|
||||
});
|
||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||
}
|
||||
|
||||
public void editorDeinit(@NotNull Editor editor) {
|
||||
@@ -253,6 +224,9 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
UserDataManager.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
||||
if (editor instanceof EditorEx editorEx) {
|
||||
editorEx.removePropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
|
||||
@@ -264,9 +238,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Element getState() {
|
||||
public @Nullable Element getState() {
|
||||
Element element = new Element("editor");
|
||||
saveData(element);
|
||||
return element;
|
||||
@@ -315,7 +288,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
@Override
|
||||
public Integer convert(@NotNull Editor editor, int lineNumber) {
|
||||
final IjVimEditor ijVimEditor = new IjVimEditor(editor);
|
||||
final boolean number = ijOptions(injector, ijVimEditor).getNumber();
|
||||
final boolean number = options(injector, ijVimEditor).getNumber();
|
||||
final int caretLine = editor.getCaretModel().getLogicalPosition().line;
|
||||
|
||||
// lineNumber is 1 based
|
||||
@@ -342,18 +315,16 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors() {
|
||||
public @NotNull Collection<VimEditor> getEditors() {
|
||||
return getLocalEditors()
|
||||
.filter(UserDataManager::getVimInitialised)
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||
public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return getLocalEditors()
|
||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||
@@ -373,7 +344,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
// events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
|
||||
// the remote editors.
|
||||
// This invocation will always get local editors, regardless of the current context.
|
||||
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
|
||||
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.LOCAL);
|
||||
if (!appSessions.isEmpty()) {
|
||||
ClientAppSession localSession = appSessions.get(0);
|
||||
return localSession.getService(ClientEditorManager.class).editors();
|
||||
@@ -382,4 +353,37 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to property changes from the editor to hide ex text field/output panel when the editor's font is zoomed
|
||||
*/
|
||||
private static class FontSizeChangeListener implements PropertyChangeListener {
|
||||
public static FontSizeChangeListener INSTANCE = new FontSizeChangeListener();
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (VimPlugin.isNotEnabled()) return;
|
||||
if (evt.getPropertyName().equals(EditorEx.PROP_FONT_SIZE)) {
|
||||
Object source = evt.getSource();
|
||||
if (source instanceof Editor editor) {
|
||||
// The editor is being zoomed, so hide the command line or output panel, if they're being shown. On the one
|
||||
// hand, it's a little rude to cancel a command line for the user, but on the other, the panels obscure the
|
||||
// zoom indicator, so it looks nicer if we hide them.
|
||||
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
|
||||
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
|
||||
if (activeCommandLine != null) {
|
||||
activeCommandLine.close(true, false);
|
||||
}
|
||||
VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
|
||||
if (outputPanel != null) {
|
||||
outputPanel.close();
|
||||
}
|
||||
VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
|
||||
if (modalInput != null) {
|
||||
modalInput.deactivate(true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Editor
|
||||
|
||||
@Service
|
||||
internal class EditorHolderService {
|
||||
var editor: Editor? = null
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): EditorHolderService = service()
|
||||
}
|
||||
}
|
@@ -1,476 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelperRt;
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper;
|
||||
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
|
||||
|
||||
public class FileGroup extends VimFileBase {
|
||||
public boolean openFile(@NotNull String filename, @NotNull ExecutionContext context) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("openFile(" + filename + ")");
|
||||
}
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext()); // API change - don't merge
|
||||
if (project == null) return false;
|
||||
|
||||
VirtualFile found = findFile(filename, project);
|
||||
|
||||
if (found != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("found file: " + found);
|
||||
}
|
||||
// Can't open a file unless it has a known file type. The next call will return the known type.
|
||||
// If unknown, IDEA will prompt the user to pick a type.
|
||||
FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project);
|
||||
|
||||
//noinspection IfStatementWithIdenticalBranches
|
||||
if (type != null) {
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project);
|
||||
fem.openFile(found, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// There was no type and user didn't pick one. Don't open the file
|
||||
// Return true here because we found the file but the user canceled by not picking a type.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable VirtualFile findFile(@NotNull String filename, @NotNull Project project) {
|
||||
VirtualFile found;
|
||||
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
||||
// it only supports forward slash on Unix (tested on Mac)
|
||||
// VFS works with both directory separators (tested on Mac and Windows)
|
||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||
String relativePath = filename.substring(2);
|
||||
String dir = System.getProperty("user.home");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("home dir file");
|
||||
logger.debug("looking for " + relativePath + " in " + dir);
|
||||
}
|
||||
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, relativePath));
|
||||
}
|
||||
else {
|
||||
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
|
||||
|
||||
if (found == null) {
|
||||
found = findByNameInContentRoots(filename, project);
|
||||
if (found == null) {
|
||||
found = findByNameInProject(filename, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private VirtualFile findByNameInContentRoots(@NotNull String filename, @NotNull Project project) {
|
||||
VirtualFile found = null;
|
||||
ProjectRootManager prm = ProjectRootManager.getInstance(project);
|
||||
VirtualFile[] roots = prm.getContentRoots();
|
||||
for (int i = 0; i < roots.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("root[" + i + "] = " + roots[i].getPath());
|
||||
}
|
||||
found = roots[i].findFileByRelativePath(filename);
|
||||
if (found != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile findByNameInProject(@NotNull String filename, @NotNull Project project) {
|
||||
GlobalSearchScope projectScope = ProjectScope.getProjectScope(project);
|
||||
Collection<VirtualFile> names = FilenameIndex.getVirtualFilesByName(filename, projectScope);
|
||||
if (!names.isEmpty()) {
|
||||
return names.stream().findFirst().get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current editor.
|
||||
*/
|
||||
@Override
|
||||
public void closeFile(@NotNull VimEditor editor, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||
if (project != null) {
|
||||
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||
final VirtualFile virtualFile = fileEditorManager.getCurrentFile();
|
||||
|
||||
if (virtualFile != null && window != null) {
|
||||
// During the work on VIM-2912 I've changed the close function to this one.
|
||||
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
||||
//window.getManager().closeFile(virtualFile, true, false);
|
||||
window.closeFile(virtualFile);
|
||||
|
||||
// Get focus after closing tab
|
||||
window.requestFocus(true);
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes editor.
|
||||
*/
|
||||
@Override
|
||||
public void closeFile(int number, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return;
|
||||
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||
VirtualFile[] editors = fileEditorManager.getOpenFiles();
|
||||
if (window != null) {
|
||||
if (number >= 0 && number < editors.length) {
|
||||
fileEditorManager.closeFile(editors[number], window);
|
||||
}
|
||||
} if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves specific file in the project.
|
||||
*/
|
||||
@Override
|
||||
public void saveFile(@NotNull ExecutionContext context) {
|
||||
NativeAction action;
|
||||
if (globalIjOptions(injector).getIdeawrite().contains(IjOptionConstants.ideawrite_all)) {
|
||||
action = injector.getNativeActionManager().getSaveAll();
|
||||
}
|
||||
else {
|
||||
action = injector.getNativeActionManager().getSaveCurrent();
|
||||
}
|
||||
ExecuteExtensionKt.execute(action, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all files in the project.
|
||||
*/
|
||||
public void saveFiles(@NotNull ExecutionContext context) {
|
||||
ExecuteExtensionKt.execute(VimInjectorKt.getInjector().getNativeActionManager().getSaveAll(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
@Override
|
||||
public boolean selectFile(int count, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return false;
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||
VirtualFile[] editors = fem.getOpenFiles();
|
||||
if (count == 99) {
|
||||
count = editors.length - 1;
|
||||
}
|
||||
if (count < 0 || count >= editors.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fem.openFile(editors[count], true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
public void selectNextFile(int count, @NotNull ExecutionContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return;
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||
VirtualFile[] editors = fem.getOpenFiles();
|
||||
VirtualFile current = fem.getSelectedFiles()[0];
|
||||
for (int i = 0; i < editors.length; i++) {
|
||||
if (editors[i].equals(current)) {
|
||||
int pos = (i + (count % editors.length) + editors.length) % editors.length;
|
||||
|
||||
fem.openFile(editors[pos], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects previous editor tab.
|
||||
*/
|
||||
@Override
|
||||
public void selectPreviousTab(@NotNull ExecutionContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||
if (project == null) return;
|
||||
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||
if (vf != null && vf.isValid()) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||
}
|
||||
else {
|
||||
VimPlugin.indicateError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous tab.
|
||||
*/
|
||||
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||
if (project == null) return null;
|
||||
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||
if (vf != null && vf.isValid()) {
|
||||
return vf;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable Editor selectEditor(Project project, @NotNull VirtualFile file) {
|
||||
FileEditorManager fMgr = FileEditorManager.getInstance(project);
|
||||
FileEditor[] feditors = fMgr.openFile(file, true);
|
||||
if (feditors.length > 0) {
|
||||
if (feditors[0] instanceof TextEditor) {
|
||||
Editor editor = ((TextEditor)feditors[0]).getEditor();
|
||||
if (!editor.isDisposed()) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayLocationInfo(@NotNull VimEditor vimEditor) {
|
||||
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
StringBuilder msg = new StringBuilder();
|
||||
Document doc = editor.getDocument();
|
||||
|
||||
if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
|
||||
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
|
||||
int endoff = doc.getLineEndOffset(lp.line);
|
||||
if (endoff < EditorHelperRt.getFileSize(editor) && doc.getCharsSequence().charAt(endoff) == '\n') {
|
||||
endoff--;
|
||||
}
|
||||
int ecol = endoff - doc.getLineStartOffset(lp.line);
|
||||
LogicalPosition elp = editor.offsetToLogicalPosition(endoff);
|
||||
|
||||
msg.append("Col ").append(col + 1);
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1);
|
||||
}
|
||||
|
||||
msg.append(" of ").append(ecol + 1);
|
||||
if (ecol != elp.column) {
|
||||
msg.append("-").append(elp.column + 1);
|
||||
}
|
||||
|
||||
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
|
||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total);
|
||||
|
||||
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||
|
||||
msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
|
||||
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
int size = EditorHelperRt.getFileSize(editor);
|
||||
|
||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size);
|
||||
}
|
||||
else {
|
||||
msg.append("Selected ");
|
||||
|
||||
TextRange vr = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
|
||||
editor.getSelectionModel().getBlockSelectionEnds());
|
||||
vr.normalize();
|
||||
|
||||
int lines;
|
||||
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||
int words = cp.getCount();
|
||||
int word = 0;
|
||||
if (vr.isMultiple()) {
|
||||
lines = vr.size();
|
||||
int cols = vr.getMaxLength();
|
||||
|
||||
msg.append(cols).append(" Cols; ");
|
||||
|
||||
for (int i = 0; i < vr.size(); i++) {
|
||||
cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
|
||||
word += cp.getCount();
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogicalPosition slp = editor.offsetToLogicalPosition(vr.getStartOffset());
|
||||
LogicalPosition elp = editor.offsetToLogicalPosition(vr.getEndOffset());
|
||||
|
||||
lines = elp.line - slp.line + 1;
|
||||
|
||||
cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
|
||||
word = cp.getCount();
|
||||
}
|
||||
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
|
||||
msg.append(lines).append(" of ").append(total).append(" Lines");
|
||||
|
||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words");
|
||||
|
||||
int chars = vr.getSelectionCount();
|
||||
int size = EditorHelperRt.getFileSize(editor);
|
||||
|
||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters");
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayFileInfo(@NotNull VimEditor vimEditor, boolean fullPath) {
|
||||
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
StringBuilder msg = new StringBuilder();
|
||||
VirtualFile vf = EditorHelper.getVirtualFile(editor);
|
||||
if (vf != null) {
|
||||
msg.append('"');
|
||||
if (fullPath) {
|
||||
msg.append(vf.getPath());
|
||||
}
|
||||
else {
|
||||
Project project = editor.getProject();
|
||||
if (project != null) {
|
||||
VirtualFile root = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(vf);
|
||||
if (root != null) {
|
||||
msg.append(vf.getPath().substring(root.getPath().length() + 1));
|
||||
}
|
||||
else {
|
||||
msg.append(vf.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.append("\" ");
|
||||
}
|
||||
else {
|
||||
msg.append("\"[No File]\" ");
|
||||
}
|
||||
|
||||
Document doc = editor.getDocument();
|
||||
if (!doc.isWritable()) {
|
||||
msg.append("[RO] ");
|
||||
}
|
||||
else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
||||
msg.append("[+] ");
|
||||
}
|
||||
|
||||
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
int pct = (int)((float)lline / (float)total * 100f + 0.5);
|
||||
|
||||
msg.append("line ").append(lline + 1).append(" of ").append(total);
|
||||
msg.append(" --").append(pct).append("%-- ");
|
||||
|
||||
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lline);
|
||||
|
||||
msg.append("col ").append(col + 1);
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1);
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString());
|
||||
}
|
||||
|
||||
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||
|
||||
/**
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
*/
|
||||
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
|
||||
if (event.getOldFile() != null) {
|
||||
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VimEditor selectEditor(@NotNull String projectId, @NotNull String documentPath, @Nullable String protocol) {
|
||||
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
|
||||
if (fileSystem == null) return null;
|
||||
VirtualFile virtualFile = fileSystem.findFileByPath(documentPath);
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
Project project = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
|
||||
.filter(p -> injector.getFile().getProjectId(p).equals(projectId))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
Editor editor = selectEditor(project, virtualFile);
|
||||
if (editor == null) return null;
|
||||
return new IjVimEditor(editor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getProjectId(@NotNull Object project) {
|
||||
if (!(project instanceof Project)) throw new IllegalArgumentException();
|
||||
return ((Project) project).getName();
|
||||
}
|
||||
}
|
444
src/main/java/com/maddyhome/idea/vim/group/FileGroup.kt
Normal file
444
src/main/java/com/maddyhome/idea/vim/group/FileGroup.kt
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimFileBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||
import com.maddyhome.idea.vim.helper.countWords
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.execute
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class FileGroup : VimFileBase() {
|
||||
override fun openFile(filename: String, context: ExecutionContext): Boolean {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("openFile($filename)")
|
||||
}
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context)
|
||||
?: return false // API change - don't merge
|
||||
|
||||
val found = findFile(filename, project)
|
||||
|
||||
if (found != null) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("found file: $found")
|
||||
}
|
||||
// Can't open a file unless it has a known file type. The next call will return the known type.
|
||||
// If unknown, IDEA will prompt the user to pick a type.
|
||||
val type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project)
|
||||
|
||||
if (type != null) {
|
||||
val fem = FileEditorManager.getInstance(project)
|
||||
fem.openFile(found, true)
|
||||
|
||||
return true
|
||||
} else {
|
||||
// There was no type and user didn't pick one. Don't open the file
|
||||
// Return true here because we found the file but the user canceled by not picking a type.
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
VimPlugin.showMessage(message("unable.to.find.0", filename))
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun findFile(filename: String, project: Project): VirtualFile? {
|
||||
var found: VirtualFile?
|
||||
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
||||
// it only supports forward slash on Unix (tested on Mac)
|
||||
// VFS works with both directory separators (tested on Mac and Windows)
|
||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||
val relativePath = filename.substring(2)
|
||||
val dir = System.getProperty("user.home")
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("home dir file")
|
||||
logger.debug("looking for $relativePath in $dir")
|
||||
}
|
||||
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(dir, relativePath))
|
||||
} else {
|
||||
found = LocalFileSystem.getInstance().findFileByIoFile(File(filename))
|
||||
|
||||
if (found == null) {
|
||||
found = findByNameInContentRoots(filename, project)
|
||||
if (found == null) {
|
||||
found = findByNameInProject(filename, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
private fun findByNameInContentRoots(filename: String, project: Project): VirtualFile? {
|
||||
var found: VirtualFile? = null
|
||||
val prm = ProjectRootManager.getInstance(project)
|
||||
val roots = prm.contentRoots
|
||||
for (i in roots.indices) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("root[" + i + "] = " + roots[i].path)
|
||||
}
|
||||
found = roots[i].findFileByRelativePath(filename)
|
||||
if (found != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current editor.
|
||||
*/
|
||||
override fun closeFile(editor: VimEditor, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext))
|
||||
if (project != null) {
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
val window = fileEditorManager.currentWindow
|
||||
val virtualFile = fileEditorManager.currentFile
|
||||
|
||||
if (virtualFile != null && window != null) {
|
||||
// During the work on VIM-2912 I've changed the close function to this one.
|
||||
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
||||
//window.getManager().closeFile(virtualFile, true, false);
|
||||
window.closeFile(virtualFile)
|
||||
|
||||
// Get focus after closing tab
|
||||
window.requestFocus(true)
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes editor.
|
||||
*/
|
||||
override fun closeFile(number: Int, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
val window = fileEditorManager.currentWindow
|
||||
val editors = fileEditorManager.openFiles
|
||||
if (window != null) {
|
||||
if (number >= 0 && number < editors.size) {
|
||||
fileEditorManager.closeFile(editors[number], window)
|
||||
}
|
||||
}
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves specific file in the project.
|
||||
*/
|
||||
override fun saveFile(editor: VimEditor, context: ExecutionContext) {
|
||||
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
|
||||
injector.nativeActionManager.saveAll
|
||||
} else {
|
||||
injector.nativeActionManager.saveCurrent
|
||||
}
|
||||
action.execute(editor, context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all files in the project.
|
||||
*/
|
||||
override fun saveFiles(editor: VimEditor, context: ExecutionContext) {
|
||||
injector.nativeActionManager.saveAll.execute(editor, context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
override fun selectFile(count: Int, context: ExecutionContext): Boolean {
|
||||
var count = count
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return false
|
||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
||||
val editors = fem.openFiles
|
||||
if (count == 99) {
|
||||
count = editors.size - 1
|
||||
}
|
||||
if (count < 0 || count >= editors.size) {
|
||||
return false
|
||||
}
|
||||
|
||||
fem.openFile(editors[count], true)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
override fun selectNextFile(count: Int, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
||||
val editors = fem.openFiles
|
||||
val current = fem.selectedFiles[0]
|
||||
for (i in editors.indices) {
|
||||
if (editors[i] == current) {
|
||||
val pos = (i + (count % editors.size) + editors.size) % editors.size
|
||||
|
||||
fem.openFile(editors[pos], true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects previous editor tab.
|
||||
*/
|
||||
override fun selectPreviousTab(context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext)) ?: return
|
||||
val vf = getInstance(project).lastTab
|
||||
if (vf != null && vf.isValid) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true)
|
||||
} else {
|
||||
VimPlugin.indicateError()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous tab.
|
||||
*/
|
||||
fun getPreviousTab(context: DataContext): VirtualFile? {
|
||||
val project = PlatformDataKeys.PROJECT.getData(context) ?: return null
|
||||
val vf = getInstance(project).lastTab
|
||||
if (vf != null && vf.isValid) {
|
||||
return vf
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun selectEditor(project: Project, file: VirtualFile): Editor? {
|
||||
val fMgr = FileEditorManager.getInstance(project)
|
||||
val feditors = fMgr.openFile(file, true)
|
||||
if (feditors.size > 0) {
|
||||
if (feditors[0] is TextEditor) {
|
||||
val editor = (feditors[0] as TextEditor).editor
|
||||
if (!editor.isDisposed) {
|
||||
return editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun displayLocationInfo(vimEditor: VimEditor) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
val doc = editor.document
|
||||
|
||||
if (injector.vimState.mode !is VISUAL) {
|
||||
val lp = editor.caretModel.logicalPosition
|
||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
||||
var endoff = doc.getLineEndOffset(lp.line)
|
||||
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
|
||||
endoff--
|
||||
}
|
||||
val ecol = endoff - doc.getLineStartOffset(lp.line)
|
||||
val elp = editor.offsetToLogicalPosition(endoff)
|
||||
|
||||
msg.append("Col ").append(col + 1)
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1)
|
||||
}
|
||||
|
||||
msg.append(" of ").append(ecol + 1)
|
||||
if (ecol != elp.column) {
|
||||
msg.append("-").append(elp.column + 1)
|
||||
}
|
||||
|
||||
val lline = editor.caretModel.logicalPosition.line
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
|
||||
|
||||
val cp = countWords(vimEditor)
|
||||
|
||||
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
|
||||
|
||||
val offset = editor.caretModel.offset
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
|
||||
} else {
|
||||
msg.append("Selected ")
|
||||
|
||||
val vr = TextRange(
|
||||
editor.selectionModel.blockSelectionStarts,
|
||||
editor.selectionModel.blockSelectionEnds
|
||||
)
|
||||
vr.normalize()
|
||||
|
||||
val lines: Int
|
||||
var cp = countWords(vimEditor)
|
||||
val words = cp.count
|
||||
var word = 0
|
||||
if (vr.isMultiple) {
|
||||
lines = vr.size()
|
||||
val cols = vr.maxLength
|
||||
|
||||
msg.append(cols).append(" Cols; ")
|
||||
|
||||
for (i in 0 until vr.size()) {
|
||||
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
|
||||
word += cp.count
|
||||
}
|
||||
} else {
|
||||
val slp = editor.offsetToLogicalPosition(vr.startOffset)
|
||||
val elp = editor.offsetToLogicalPosition(vr.endOffset)
|
||||
|
||||
lines = elp.line - slp.line + 1
|
||||
|
||||
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
|
||||
word = cp.count
|
||||
}
|
||||
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append(lines).append(" of ").append(total).append(" Lines")
|
||||
|
||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
|
||||
|
||||
val chars = vr.selectionCount
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString())
|
||||
}
|
||||
|
||||
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
val vf = EditorHelper.getVirtualFile(editor)
|
||||
if (vf != null) {
|
||||
msg.append('"')
|
||||
if (fullPath) {
|
||||
msg.append(vf.path)
|
||||
} else {
|
||||
val project = editor.project
|
||||
if (project != null) {
|
||||
val root = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(vf)
|
||||
if (root != null) {
|
||||
msg.append(vf.path.substring(root.path.length + 1))
|
||||
} else {
|
||||
msg.append(vf.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.append("\" ")
|
||||
} else {
|
||||
msg.append("\"[No File]\" ")
|
||||
}
|
||||
|
||||
val doc = editor.document
|
||||
if (!doc.isWritable) {
|
||||
msg.append("[RO] ")
|
||||
} else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
||||
msg.append("[+] ")
|
||||
}
|
||||
|
||||
val lline = editor.caretModel.logicalPosition.line
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
val pct = (lline.toFloat() / total.toFloat() * 100f + 0.5).toInt()
|
||||
|
||||
msg.append("line ").append(lline + 1).append(" of ").append(total)
|
||||
msg.append(" --").append(pct).append("%-- ")
|
||||
|
||||
val lp = editor.caretModel.logicalPosition
|
||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lline)
|
||||
|
||||
msg.append("col ").append(col + 1)
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1)
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString())
|
||||
}
|
||||
|
||||
override fun selectEditor(projectId: String, documentPath: String, protocol: String?): VimEditor? {
|
||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol) ?: return null
|
||||
val virtualFile = fileSystem.findFileByPath(documentPath) ?: return null
|
||||
|
||||
val project = Arrays.stream(ProjectManager.getInstance().openProjects)
|
||||
.filter { p: Project? -> injector.file.getProjectId(p!!) == projectId }
|
||||
.findFirst().orElseThrow()
|
||||
|
||||
val editor = selectEditor(project, virtualFile) ?: return null
|
||||
return IjVimEditor(editor)
|
||||
}
|
||||
|
||||
override fun getProjectId(project: Any): String {
|
||||
require(project is Project)
|
||||
return project.name + "-" + project.locationHash
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun findByNameInProject(filename: String, project: Project): VirtualFile? {
|
||||
val projectScope = ProjectScope.getProjectScope(project)
|
||||
val names = FilenameIndex.getVirtualFilesByName(filename, projectScope)
|
||||
if (!names.isEmpty()) {
|
||||
return names.stream().findFirst().get()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val logger = Logger.getInstance(
|
||||
FileGroup::class.java.name
|
||||
)
|
||||
|
||||
/**
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
*/
|
||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||
if (event.oldFile != null) {
|
||||
getInstance(event.manager.project).lastTab = event.oldFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,9 +14,7 @@ import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.history.HistoryBlock;
|
||||
import com.maddyhome.idea.vim.history.HistoryEntry;
|
||||
import com.maddyhome.idea.vim.history.VimHistoryBase;
|
||||
import com.maddyhome.idea.vim.history.*;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -35,21 +33,20 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
||||
logger.debug("saveData");
|
||||
Element hist = new Element("history");
|
||||
|
||||
saveData(hist, SEARCH);
|
||||
saveData(hist, COMMAND);
|
||||
saveData(hist, EXPRESSION);
|
||||
saveData(hist, INPUT);
|
||||
for (Type type : getHistories().keySet()) {
|
||||
saveData(hist, type);
|
||||
}
|
||||
|
||||
element.addContent(hist);
|
||||
}
|
||||
|
||||
private void saveData(@NotNull Element element, String key) {
|
||||
final HistoryBlock block = getHistories().get(key);
|
||||
private void saveData(@NotNull Element element, VimHistory.Type type) {
|
||||
final HistoryBlock block = getHistories().get(type);
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Element root = new Element("history-" + key);
|
||||
final Element root = new Element("history-" + typeToKey(type));
|
||||
|
||||
for (HistoryEntry entry : block.getEntries()) {
|
||||
final Element entryElement = new Element("entry");
|
||||
@@ -67,10 +64,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
||||
return;
|
||||
}
|
||||
|
||||
readData(hist, SEARCH);
|
||||
readData(hist, COMMAND);
|
||||
readData(hist, EXPRESSION);
|
||||
readData(hist, INPUT);
|
||||
for (Element child : hist.getChildren()) {
|
||||
String key = child.getName().replace("history-", "");
|
||||
readData(hist, key);
|
||||
}
|
||||
}
|
||||
|
||||
private void readData(@NotNull Element element, String key) {
|
||||
@@ -80,7 +77,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
||||
}
|
||||
|
||||
block = new HistoryBlock();
|
||||
getHistories().put(key, block);
|
||||
getHistories().put(getTypeForString(key), block);
|
||||
|
||||
final Element root = element.getChild("history-" + key);
|
||||
if (root != null) {
|
||||
@@ -94,6 +91,25 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
||||
}
|
||||
}
|
||||
|
||||
private String typeToKey(VimHistory.Type type) {
|
||||
if (type instanceof VimHistory.Type.Search) {
|
||||
return SEARCH;
|
||||
}
|
||||
if (type instanceof VimHistory.Type.Command) {
|
||||
return COMMAND;
|
||||
}
|
||||
if (type instanceof VimHistory.Type.Expression) {
|
||||
return EXPRESSION;
|
||||
}
|
||||
if (type instanceof VimHistory.Type.Input) {
|
||||
return INPUT;
|
||||
}
|
||||
if (type instanceof VimHistory.Type.Custom) {
|
||||
return ((Type.Custom) type).getId();
|
||||
}
|
||||
return "unreachable";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Element getState() {
|
||||
|
@@ -19,24 +19,22 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
* options
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||
public var ide: String by optionProperty(IjOptions.ide)
|
||||
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||
public val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
||||
public var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
||||
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||
public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||
open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||
var ide: String by optionProperty(IjOptions.ide)
|
||||
var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||
var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||
val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
||||
var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
||||
val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||
var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||
var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||
|
||||
// Temporary options to control work-in-progress behaviour
|
||||
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,20 +42,19 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
|
||||
*
|
||||
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
||||
*/
|
||||
public 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
|
||||
public var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
||||
public val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
||||
public var cursorline: Boolean by optionProperty(IjOptions.cursorline)
|
||||
public var fileformat: String by optionProperty(IjOptions.fileformat)
|
||||
public var list: Boolean by optionProperty(IjOptions.list)
|
||||
public var number: Boolean by optionProperty(IjOptions.number)
|
||||
public var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
|
||||
public var textwidth: Int by optionProperty(IjOptions.textwidth)
|
||||
public var wrap: Boolean by optionProperty(IjOptions.wrap)
|
||||
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
||||
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
||||
var cursorline: Boolean by optionProperty(IjOptions.cursorline)
|
||||
var fileformat: String by optionProperty(IjOptions.fileformat)
|
||||
var list: Boolean by optionProperty(IjOptions.list)
|
||||
var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
|
||||
var textwidth: Int by optionProperty(IjOptions.textwidth)
|
||||
var wrap: Boolean by optionProperty(IjOptions.wrap)
|
||||
|
||||
// IntelliJ specific options
|
||||
public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
||||
public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
||||
public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
||||
var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
||||
var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
||||
var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
||||
}
|
||||
|
@@ -26,9 +26,9 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public object IjOptions {
|
||||
object IjOptions {
|
||||
|
||||
public fun initialise() {
|
||||
fun initialise() {
|
||||
// Calling this method allows for deterministic initialisation of IjOptions, specifically initialising the
|
||||
// properties and registering the IJ specific options. Once added, they can be safely accessed by name, e.g. by the
|
||||
// implementation of `:set` while executing ~/.ideavimrc
|
||||
@@ -39,8 +39,8 @@ public object IjOptions {
|
||||
}
|
||||
|
||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
||||
public val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
|
||||
public val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
|
||||
val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
|
||||
val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
|
||||
override fun checkIfValueValid(value: VimDataType, token: String) {
|
||||
super.checkIfValueValid(value, token)
|
||||
if (value != VimString.EMPTY) {
|
||||
@@ -55,19 +55,18 @@ public object IjOptions {
|
||||
}
|
||||
}
|
||||
})
|
||||
public val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
|
||||
public val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
|
||||
public val number: ToggleOption = addOption(ToggleOption("number", LOCAL_TO_WINDOW, "nu", false))
|
||||
public val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
|
||||
public val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
|
||||
public val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
|
||||
val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
|
||||
val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
|
||||
val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
|
||||
val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
|
||||
val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
|
||||
|
||||
// These options are not explicitly listed as local-noglobal in Vim's help, but are set when a new buffer is edited,
|
||||
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file cnversion, we treat them as
|
||||
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file conversion, we treat them as
|
||||
// local-noglobal. See `:help local-noglobal`, `:help 'fileformats'` and `:help 'fileencodings'`
|
||||
public val bomb: ToggleOption =
|
||||
val bomb: ToggleOption =
|
||||
addOption(ToggleOption("bomb", LOCAL_TO_BUFFER, "bomb", false, isLocalNoGlobal = true))
|
||||
public val fileencoding: StringOption = addOption(
|
||||
val fileencoding: StringOption = addOption(
|
||||
StringOption(
|
||||
"fileencoding",
|
||||
LOCAL_TO_BUFFER,
|
||||
@@ -76,7 +75,7 @@ public object IjOptions {
|
||||
isLocalNoGlobal = true
|
||||
)
|
||||
)
|
||||
public val fileformat: StringOption = addOption(
|
||||
val fileformat: StringOption = addOption(
|
||||
StringOption(
|
||||
"fileformat",
|
||||
LOCAL_TO_BUFFER,
|
||||
@@ -88,15 +87,15 @@ public object IjOptions {
|
||||
)
|
||||
|
||||
// IntelliJ specific functionality - custom options
|
||||
public val ide: StringOption = addOption(
|
||||
val ide: StringOption = addOption(
|
||||
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
||||
)
|
||||
public val ideacopypreprocess: ToggleOption = addOption(
|
||||
val ideacopypreprocess: ToggleOption = addOption(
|
||||
ToggleOption("ideacopypreprocess", GLOBAL_OR_LOCAL_TO_BUFFER, "ideacopypreprocess", false)
|
||||
)
|
||||
public val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
||||
public val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
||||
public val idearefactormode: StringOption = addOption(
|
||||
val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
||||
val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
||||
val idearefactormode: StringOption = addOption(
|
||||
StringOption(
|
||||
"idearefactormode",
|
||||
GLOBAL_OR_LOCAL_TO_BUFFER,
|
||||
@@ -105,7 +104,7 @@ public object IjOptions {
|
||||
IjOptionConstants.ideaRefactorModeValues
|
||||
)
|
||||
)
|
||||
public val ideastatusicon: StringOption = addOption(
|
||||
val ideastatusicon: StringOption = addOption(
|
||||
StringOption(
|
||||
"ideastatusicon",
|
||||
GLOBAL,
|
||||
@@ -114,7 +113,7 @@ public object IjOptions {
|
||||
IjOptionConstants.ideaStatusIconValues
|
||||
)
|
||||
)
|
||||
public val ideavimsupport: StringListOption = addOption(
|
||||
val ideavimsupport: StringListOption = addOption(
|
||||
StringListOption(
|
||||
"ideavimsupport",
|
||||
GLOBAL,
|
||||
@@ -123,27 +122,26 @@ public object IjOptions {
|
||||
IjOptionConstants.ideavimsupportValues
|
||||
)
|
||||
)
|
||||
@JvmField public val ideawrite: StringOption = addOption(
|
||||
@JvmField
|
||||
val ideawrite: StringOption = addOption(
|
||||
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
||||
)
|
||||
public val lookupkeys: StringListOption = addOption(
|
||||
val lookupkeys: StringListOption = addOption(
|
||||
StringListOption(
|
||||
"lookupkeys",
|
||||
GLOBAL,
|
||||
"lookupkeys",
|
||||
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
||||
)
|
||||
public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||
public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||
|
||||
// Temporary feature flags during development, not really intended for external use
|
||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||
val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, 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 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
|
||||
// derives from Option<VimInt>
|
||||
|
@@ -21,12 +21,12 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
|
||||
@Service
|
||||
public class IjVimPsiService: VimPsiService {
|
||||
class IjVimPsiService: VimPsiService {
|
||||
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
|
||||
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
||||
val psiElement = psiFile.findElementAt(pos) ?: return null
|
||||
val language = psiElement.language
|
||||
val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
|
||||
val commenter = LanguageCommenters.INSTANCE.forLanguage(language) ?: return null
|
||||
val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
|
||||
val commentText = psiComment.text
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimRedrawService
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
|
||||
public class IjVimRedrawService : VimRedrawService {
|
||||
class IjVimRedrawService : VimRedrawService {
|
||||
override fun redraw() {
|
||||
// The only thing IntelliJ needs to redraw is the status line. Everything else is handled automatically.
|
||||
redrawStatusLine()
|
||||
@@ -25,11 +25,11 @@ public class IjVimRedrawService : VimRedrawService {
|
||||
injector.messages.clearStatusBarMessage()
|
||||
}
|
||||
|
||||
public companion object {
|
||||
companion object {
|
||||
/**
|
||||
* Simulate Vim's redraw when the current editor changes
|
||||
*/
|
||||
public fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||
injector.redrawService.redraw()
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class IjVimRedrawService : VimRedrawService {
|
||||
*
|
||||
* Only redraw if lines are added/removed.
|
||||
*/
|
||||
public object RedrawListener : DocumentListener {
|
||||
object RedrawListener : DocumentListener {
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
if (VimPlugin.isNotEnabled()) return
|
||||
if (event.newFragment.contains("\n") || event.oldFragment.contains("\n")) {
|
||||
|
@@ -26,10 +26,7 @@ import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
|
||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
|
||||
import com.maddyhome.idea.vim.api.NativeAction;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.key.*;
|
||||
@@ -80,25 +77,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
((IjVimEditor)editor).getEditor().getComponent());
|
||||
}
|
||||
|
||||
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull Editor editor) {
|
||||
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
|
||||
MappingInfo mappingInfo = row.getSecond();
|
||||
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
|
||||
builder.append(" ");
|
||||
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
|
||||
builder.append(" ");
|
||||
builder.append(mappingInfo.isRecursive() ? " " : "*");
|
||||
builder.append(" ");
|
||||
builder.append(mappingInfo.getPresentableString());
|
||||
builder.append("\n");
|
||||
}
|
||||
ExOutputModel.getInstance(editor).output(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateShortcutKeysRegistration() {
|
||||
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
||||
@@ -358,6 +336,22 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
|
||||
@Override
|
||||
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) {
|
||||
return showKeyMappings(modes, ((IjVimEditor) editor).getEditor());
|
||||
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
|
||||
MappingInfo mappingInfo = row.getSecond();
|
||||
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
|
||||
builder.append(" ");
|
||||
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
|
||||
builder.append(" ");
|
||||
builder.append(mappingInfo.isRecursive() ? " " : "*");
|
||||
builder.append(" ");
|
||||
builder.append(mappingInfo.getPresentableString());
|
||||
builder.append("\n");
|
||||
}
|
||||
VimOutputPanel outputPanel = injector.getOutputPanel().getOrCreate(editor, injector.getExecutionContextManager().getEditorExecutionContext(editor));
|
||||
outputPanel.addText(builder.toString(), true);
|
||||
outputPanel.show();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -47,12 +47,14 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
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.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
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 kotlin.math.max
|
||||
import kotlin.math.min
|
||||
@@ -307,19 +309,32 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
val editor = fileEditor.editor
|
||||
if (!editor.isDisposed) {
|
||||
editor.vim.let { vimEditor ->
|
||||
when (vimEditor.vimStateMachine.mode) {
|
||||
when (vimEditor.mode) {
|
||||
is Mode.VISUAL -> {
|
||||
vimEditor.exitVisualMode()
|
||||
KeyHandler.getInstance().reset(vimEditor)
|
||||
}
|
||||
is Mode.CMD_LINE -> {
|
||||
injector.processGroup.cancelExEntry(vimEditor, false)
|
||||
ExOutputModel.getInstance(editor).clear()
|
||||
val commandLine = injector.commandLine.getActiveCommandLine() ?: return
|
||||
commandLine.close(refocusOwningEditor = false, resetCaret = false)
|
||||
injector.outputPanel.getCurrentOutputPanel()?.close()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val state = injector.vimState as VimStateMachineImpl
|
||||
if (state.mode is Mode.VISUAL) {
|
||||
val returnTo = 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()
|
||||
KeyHandler.getInstance().reset(keyHandler.keyHandlerState, state.mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -62,6 +62,25 @@ internal class NotificationService(private val project: Project?) {
|
||||
@Suppress("unused")
|
||||
constructor() : this(null)
|
||||
|
||||
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") {
|
||||
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 notifyAboutIdeaPut() {
|
||||
val notification = Notification(
|
||||
IDEAVIM_NOTIFICATION_ID,
|
||||
@@ -182,8 +201,8 @@ internal class NotificationService(private val project: Project?) {
|
||||
).notify(project)
|
||||
}
|
||||
|
||||
fun notifyActionId(id: String?) {
|
||||
ActionIdNotifier.notifyActionId(id, project)
|
||||
fun notifyActionId(id: String?, candidates: List<String>? = null) {
|
||||
ActionIdNotifier.notifyActionId(id, project, candidates)
|
||||
}
|
||||
|
||||
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
|
||||
@@ -259,20 +278,31 @@ internal class NotificationService(private val project: Project?) {
|
||||
|
||||
object ActionIdNotifier {
|
||||
private var notification: Notification? = null
|
||||
private const val NO_ID = "<i>Cannot detect action id</i>"
|
||||
|
||||
fun notifyActionId(id: String?, project: Project?) {
|
||||
fun notifyActionId(id: String?, project: Project?, candidates: List<String>? = null) {
|
||||
notification?.expire()
|
||||
|
||||
val content = if (id != null) "Action id: $id" else NO_ID
|
||||
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).let {
|
||||
notification = it
|
||||
it.whenExpired { notification = null }
|
||||
it.setContent(it.content + "<br><br><small>Use ${ActionCenter.getToolwindowName()} to see previous ids</small>")
|
||||
val possibleIDs = candidates?.distinct()?.sorted()
|
||||
val content = when {
|
||||
id != null -> "Action ID: <code>$id</code><br><br>"
|
||||
possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
|
||||
possibleIDs.size == 1 -> "Possible action ID: <code>${possibleIDs[0]}</code><br><br>"
|
||||
else -> {
|
||||
buildString {
|
||||
append("<p>Multiple possible action IDs. Candidates include:<ul>")
|
||||
possibleIDs.forEach { append("<li><code>$it</code></li>") }
|
||||
append("</ul></p>")
|
||||
}
|
||||
}
|
||||
} + "<small>See the ${ActionCenter.getToolwindowName()} tool window for previous IDs</small>"
|
||||
|
||||
notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).also {
|
||||
it.whenExpired { notification = null }
|
||||
it.addAction(StopTracking())
|
||||
|
||||
if (id != null) it.addAction(CopyActionId(id, project))
|
||||
if (id != null || possibleIDs?.size == 1) {
|
||||
it.addAction(CopyActionId(id ?: possibleIDs?.get(0), project))
|
||||
}
|
||||
|
||||
it.notify(project)
|
||||
}
|
||||
|
@@ -12,10 +12,12 @@ import com.intellij.application.options.CodeStyle
|
||||
import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.EditorKind
|
||||
import com.intellij.openapi.editor.EditorSettings.LineNumerationType
|
||||
import com.intellij.openapi.editor.ScrollPositionCalculator
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
@@ -94,12 +96,12 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup, InternalOpt
|
||||
addOptionValueOverride(IjOptions.fileencoding, FileEncodingOptionMapper())
|
||||
addOptionValueOverride(IjOptions.fileformat, FileFormatOptionMapper())
|
||||
addOptionValueOverride(IjOptions.list, ListOptionMapper(IjOptions.list, this))
|
||||
addOptionValueOverride(IjOptions.number, NumberOptionMapper(IjOptions.number, this))
|
||||
addOptionValueOverride(IjOptions.relativenumber, RelativeNumberOptionMapper(IjOptions.relativenumber, this))
|
||||
addOptionValueOverride(IjOptions.textwidth, TextWidthOptionMapper(IjOptions.textwidth))
|
||||
addOptionValueOverride(IjOptions.wrap, WrapOptionMapper(IjOptions.wrap, this))
|
||||
|
||||
// These options are defined and implemented in vim-engine, but IntelliJ has similar features with settings we can map
|
||||
addOptionValueOverride(Options.number, NumberOptionMapper(Options.number, this))
|
||||
addOptionValueOverride(Options.scrolljump, ScrollJumpOptionMapper(Options.scrolljump, this))
|
||||
addOptionValueOverride(Options.sidescroll, SideScrollOptionMapper(Options.sidescroll, this))
|
||||
addOptionValueOverride(Options.scrolloff, ScrollOffOptionMapper(Options.scrolloff, this))
|
||||
@@ -926,7 +928,9 @@ private class ScrollJumpOptionMapper(option: NumberOption, internalOptionValueAc
|
||||
override fun getEffectiveExternalValue(editor: VimEditor) = editor.ij.settings.verticalScrollJump.asVimInt()
|
||||
|
||||
override fun setLocalExternalValue(editor: VimEditor, value: VimInt) {
|
||||
editor.ij.settings.verticalScrollJump = value.value
|
||||
// Note that Vim supports -1 to -100 as a percentage value. IntelliJ does not have any validation, but does not
|
||||
// handle or expect negative values
|
||||
editor.ij.settings.verticalScrollJump = value.value.coerceAtLeast(0)
|
||||
}
|
||||
|
||||
override fun resetLocalExternalValue(editor: VimEditor, defaultValue: VimInt) {
|
||||
@@ -975,26 +979,35 @@ private class SideScrollOptionMapper(option: NumberOption, internalOptionValueAc
|
||||
* setting value, and there is no UI to modify the local IntelliJ settings. Once the value has been set in IdeaVim, it
|
||||
* takes precedence over the global, persistent setting until the option is reset with either `:set scrolloff&` or
|
||||
* `:setlocal scrolloff<`.
|
||||
*
|
||||
* Note that when the IdeaVim value is set, we set the IntelliJ local value to 0 rather than sharing the value. This is
|
||||
* to prevent conflicts between IntelliJ and IdeaVim's separate implementations for scrolling. IntelliJ's scrolling
|
||||
* includes virtual space at the bottom of the file, while (Idea)Vim doesn't. Combining this with a non-zero
|
||||
* `'scrolloff'` value can reposition the bottom of the file. E.g., using `G` will position the last line at the bottom
|
||||
* of the file, but then IntelliJ moves it up `'scrolloff'` when the caret is moved.
|
||||
*
|
||||
* With a large value like `999`, IntelliJ will try to move the current line to the centre of the screen, but then
|
||||
* IdeaVim will try to reposition. Normally, this doesn't cause too much of a problem, because setting the scroll
|
||||
* position will cancel any outstanding animations. However, using backspace updates the scroll position with animations
|
||||
* disabled, so the scroll happens immediately, with a visible "twitch" as the editor scrolls for IntelliJ and then back
|
||||
* for IdeaVim.
|
||||
*
|
||||
* We should consider implementing [ScrollPositionCalculator] which would allow IdeaVim to completely take over
|
||||
* scrolling from IntelliJ. This would be a non-trivial change, and it might be better to move the scrolling to
|
||||
* vim-engine so it can also work in Fleet.
|
||||
*/
|
||||
private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAccessor: InternalOptionValueAccessor)
|
||||
: GlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(option, internalOptionValueAccessor) {
|
||||
private class ScrollOffOptionMapper(
|
||||
scrollOffOption: NumberOption,
|
||||
internalOptionValueAccessor: InternalOptionValueAccessor,
|
||||
) : OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(scrollOffOption, internalOptionValueAccessor) {
|
||||
|
||||
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_VERTICAL_SCROLL_OFFSET
|
||||
|
||||
// The IntelliJ setting is in practice global. The base implementation relies on this fact
|
||||
override val canUserModifyExternalLocalValue: Boolean = false
|
||||
override fun getExternalGlobalValue() =
|
||||
EditorSettingsExternalizable.getInstance().verticalScrollOffset.asVimInt()
|
||||
|
||||
override fun getGlobalExternalValue() = EditorSettingsExternalizable.getInstance().verticalScrollOffset.asVimInt()
|
||||
override fun getEffectiveExternalValue(editor: VimEditor) = editor.ij.settings.verticalScrollOffset.asVimInt()
|
||||
|
||||
override fun setLocalExternalValue(editor: VimEditor, value: VimInt) {
|
||||
editor.ij.settings.verticalScrollOffset = value.value
|
||||
}
|
||||
|
||||
override fun removeLocalExternalValue(editor: VimEditor) {
|
||||
// Unexpectedly, verticalScrollOffset accepts `-1` as a value to clear any local overrides, and this will reset the
|
||||
// effective value to return the global value
|
||||
editor.ij.settings.verticalScrollOffset = -1
|
||||
override fun suppressExternalLocalValue(editor: VimEditor) {
|
||||
editor.ij.settings.verticalScrollOffset = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,15 +1015,14 @@ private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAcc
|
||||
/**
|
||||
* Map the `'sidescrolloff'` global-local Vim option to the IntelliJ global-local horizontal scroll offset setting
|
||||
*
|
||||
* Ideally, we would implement this in a similar manner to [SideScrollOptionMapper], setting the external local
|
||||
* horizontal scroll offset value when the user explicitly sets the Vim value, so that IntelliJ could also use the
|
||||
* value. Unfortunately, IntelliJ's scrolling calculation logic is based on integer font width maths, which causes
|
||||
* problems with fractional font widths (such as on a Mac when running tests).
|
||||
* IntelliJ supports horizontal scroll offset in a similar manner to Vim. However, the implementation calculates offsets
|
||||
* using integer font sizes, which can lead to minor inaccuracies when compared to the IdeaVim implementation, such as
|
||||
* differences running tests on a Mac.
|
||||
*
|
||||
* For example, given a `'sidescrolloff'` value of `10`, and a fractional font width of `7.8`, IntelliJ will scroll `80`
|
||||
* pixels instead of `78`. This is a very minor difference, but because it overshoots, it means that IdeaVim doesn't
|
||||
* need to scroll, which in turn can cause issues with `'sidescroll'`, because IntelliJ doesn't support `sidescroll=0`,
|
||||
* which would scroll to position the caret in the middle of the display.
|
||||
* need to scroll, which in turn can cause issues with `'sidescroll'` (jump), because IntelliJ doesn't support
|
||||
* `sidescroll=0`, which would scroll to position the caret in the middle of the display.
|
||||
*
|
||||
* It also causes precision problems in the tests. The display is scrolled to a couple of pixels _before_ the leftmost
|
||||
* column, which means the rightmost column ends a couple of pixels _after_ the rightmost edge of the display. The tests
|
||||
@@ -1028,78 +1040,98 @@ private class ScrollOffOptionMapper(option: NumberOption, internalOptionValueAcc
|
||||
* vim-engine so it can also work in Fleet.
|
||||
*/
|
||||
private class SideScrollOffOptionMapper(
|
||||
private val sideScrollOffOption: NumberOption,
|
||||
private val internalOptionValueAccessor: InternalOptionValueAccessor,
|
||||
) : GlobalOptionValueOverride<VimInt>, LocalOptionValueOverride<VimInt>, IdeaBackedOptionValueOverride {
|
||||
sideScrollOffOption: NumberOption,
|
||||
internalOptionValueAccessor: InternalOptionValueAccessor,
|
||||
) : OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<VimInt>(sideScrollOffOption, internalOptionValueAccessor) {
|
||||
|
||||
override val ideaPropertyName: String = EditorSettingsExternalizable.PropNames.PROP_HORIZONTAL_SCROLL_OFFSET
|
||||
|
||||
override fun getGlobalValue(storedValue: OptionValue<VimInt>, editor: VimEditor?): OptionValue<VimInt> {
|
||||
override fun getExternalGlobalValue() =
|
||||
EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt()
|
||||
|
||||
override fun suppressExternalLocalValue(editor: VimEditor) {
|
||||
editor.ij.settings.horizontalScrollOffset = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract base class to map a global-local IDEA setting to a global-local Vim option. The IDEA setting is not
|
||||
* updated to reflect the Vim changes, but is kept at a neutral value.
|
||||
*
|
||||
* This class is used for Vim options that have an IDEA equivalent, but the implementation is handled by IdeaVim, e.g.,
|
||||
* scroll jumps and offsets. The IDEA value is not updated, and kept to a neutral value, so that the IDEA implementation
|
||||
* does not interfere with the IdeaVim implementation.
|
||||
*/
|
||||
private abstract class OneWayGlobalLocalOptionToGlobalLocalIdeaSettingMapper<T : VimDataType>(
|
||||
private val option: Option<T>,
|
||||
private val internalOptionValueAccessor: InternalOptionValueAccessor,
|
||||
) : GlobalOptionValueOverride<T>, LocalOptionValueOverride<T>, IdeaBackedOptionValueOverride {
|
||||
|
||||
override fun getGlobalValue(storedValue: OptionValue<T>, editor: VimEditor?): OptionValue<T> {
|
||||
if (storedValue is OptionValue.Default) {
|
||||
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
|
||||
return OptionValue.Default(getExternalGlobalValue())
|
||||
}
|
||||
|
||||
// If it's not the default value, it's got to be the stored value
|
||||
return storedValue
|
||||
}
|
||||
|
||||
override fun setGlobalValue(
|
||||
storedValue: OptionValue<VimInt>,
|
||||
newValue: OptionValue<VimInt>,
|
||||
editor: VimEditor?,
|
||||
): Boolean {
|
||||
// The user has typed `:setlocal`. Just make sure that the IntelliJ value doesn't interfere with the Vim value
|
||||
injector.editorGroup.getEditors().forEach { it.ij.settings.horizontalScrollOffset = 0 }
|
||||
override fun setGlobalValue(storedValue: OptionValue<T>, newValue: OptionValue<T>, editor: VimEditor?): Boolean {
|
||||
// The user is updating the global Vim value, via `:setglobal`. IdeaVim scrolling will be using this value. Make
|
||||
// sure the IntelliJ values won't interfere
|
||||
// Note that we don't reset the local IntelliJ value for `:set {option}&` or `:set {option}<` because the current
|
||||
// global IntelliJ value might still interfere with IdeaVim's implementation. We continue to suppress the IntelliJ
|
||||
// value.
|
||||
injector.editorGroup.getEditors().forEach { suppressExternalLocalValue(it) }
|
||||
return storedValue.value != newValue.value
|
||||
}
|
||||
|
||||
override fun getLocalValue(storedValue: OptionValue<VimInt>?, editor: VimEditor): OptionValue<VimInt> {
|
||||
override fun getLocalValue(storedValue: OptionValue<T>?, editor: VimEditor): OptionValue<T> {
|
||||
if (storedValue == null) {
|
||||
// Initialisation. Report the global value of the setting. We ignore the local value because the user doesn't have
|
||||
// a way to set it, and we set it to 0 so that it doesn't affect our scroll calculations (because IntelliJ doesn't
|
||||
// handle sidescroll=0 to mean half a page)
|
||||
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
|
||||
// a way to set it. If it has been changed (unlikely if stored value hasn't been set yet), then it would be 0
|
||||
return OptionValue.Default(getExternalGlobalValue())
|
||||
}
|
||||
|
||||
if (storedValue is OptionValue.Default && storedValue.value != sideScrollOffOption.unsetValue) {
|
||||
// The local value is set to the default value (as a copy of the global value), so return the global external
|
||||
// value as a default
|
||||
return OptionValue.Default(EditorSettingsExternalizable.getInstance().horizontalScrollOffset.asVimInt())
|
||||
if (storedValue is OptionValue.Default && storedValue.value != option.unsetValue) {
|
||||
// The local value has been reset to Default. It's not the Vim default of "unset", but a copy of the global value.
|
||||
// Return the current value of the global external value
|
||||
return OptionValue.Default(getExternalGlobalValue())
|
||||
}
|
||||
|
||||
// Whatever is left is either explicitly set by the user, or option.unsetValue
|
||||
return storedValue
|
||||
}
|
||||
|
||||
override fun setLocalValue(
|
||||
storedValue: OptionValue<VimInt>?,
|
||||
newValue: OptionValue<VimInt>,
|
||||
editor: VimEditor,
|
||||
): Boolean {
|
||||
// This is setting the Vim local value. We do nothing but reset the local horizontal scroll jump so IntelliJ's
|
||||
// scrolling doesn't affect our scrolling
|
||||
editor.ij.settings.horizontalScrollOffset = 0
|
||||
override fun setLocalValue(storedValue: OptionValue<T>?, newValue: OptionValue<T>, editor: VimEditor): Boolean {
|
||||
// Vim local value is being set. We do nothing but set the local IntelliJ value to 0, so IntelliJ's scrolling
|
||||
// doesn't affect IdeaVim's scrolling
|
||||
suppressExternalLocalValue(editor)
|
||||
return storedValue?.value != newValue.value
|
||||
}
|
||||
|
||||
override fun onGlobalIdeaValueChanged(propertyName: String) {
|
||||
if (propertyName == ideaPropertyName) {
|
||||
// Again, just make sure the IntelliJ local value is 0
|
||||
injector.editorGroup.getEditors().forEach { it.ij.settings.horizontalScrollOffset = 0 }
|
||||
// The IntelliJ global value has changed. We want to use this as the Vim global value. Since we control scrolling,
|
||||
// set the local IntelliJ value to 0
|
||||
injector.editorGroup.getEditors().forEach { suppressExternalLocalValue(it) }
|
||||
|
||||
// Update the stored Vim global value. This will not override any existing local values
|
||||
// Now update the Vim global value to match the new IntelliJ global value. If the current Vim global value is
|
||||
// Default, then it will already reflect the current global external value. Otherwise, update the Vim global value
|
||||
// to the external global value.
|
||||
val globalScope = OptionAccessScope.GLOBAL(null)
|
||||
val storedValue = internalOptionValueAccessor.getOptionValueInternal(sideScrollOffOption, globalScope)
|
||||
val storedValue = internalOptionValueAccessor.getOptionValueInternal(option, globalScope)
|
||||
if (storedValue !is OptionValue.Default) {
|
||||
val externalGlobalValue = EditorSettingsExternalizable.getInstance().horizontalScrollOffset
|
||||
internalOptionValueAccessor.setOptionValueInternal(
|
||||
sideScrollOffOption,
|
||||
option,
|
||||
globalScope,
|
||||
OptionValue.External(VimInt(externalGlobalValue))
|
||||
OptionValue.External(getExternalGlobalValue())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getExternalGlobalValue(): T
|
||||
protected abstract fun suppressExternalLocalValue(editor: VimEditor)
|
||||
}
|
||||
|
||||
|
||||
@@ -1198,23 +1230,63 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
|
||||
setIsUseSoftWraps(editor, value.asBoolean())
|
||||
}
|
||||
|
||||
private fun getGlobalIsUseSoftWraps(editor: VimEditor): Boolean {
|
||||
val settings = EditorSettingsExternalizable.getInstance()
|
||||
if (settings.isUseSoftWraps) {
|
||||
val masks = settings.softWrapFileMasks
|
||||
if (masks.trim() == "*") return true
|
||||
override fun canInitialiseOptionFrom(sourceEditor: VimEditor, targetEditor: VimEditor): Boolean {
|
||||
// IntelliJ's soft-wrap settings are based on editor kind, so there can be different wrap options for consoles,
|
||||
// main editors, etc. This is particularly noticeable in the console when running an application. The main editor
|
||||
// might have the Vim default with line wrap enabled. Initialising the run console will also have a default value,
|
||||
// and won't be updated by the options subsystem. It might have wrap enabled or not. If the editors were the same
|
||||
// kind, the same default value would be used.
|
||||
// However, if the main editor has 'wrap' explicitly set, this value is copied to the console, so the behaviour is
|
||||
// inconsistent. Furthermore, the run console has a soft-wraps toggle button that works at the global level, and
|
||||
// IdeaVim only sets the local value, so the toggle button can be inconsistent too.
|
||||
// By denying copying the main editor value during initialisation, the console gets the default value, and the IDE
|
||||
// decides what it should be. The behaviour is now more consistent.
|
||||
// We're happy to initialise diff editors from main editors, as there isn't a different soft wrap setting there.
|
||||
// Preview tabs might also have different settings, but because they're a type of main editor, it doesn't matter
|
||||
// so much
|
||||
fun editorKindToSoftWrapAppliancesPlace(kind: EditorKind) = when (kind) {
|
||||
EditorKind.UNTYPED,
|
||||
EditorKind.DIFF,
|
||||
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
||||
// Treat PREVIEW as a kind of MAIN_EDITOR instead of SWAP.PREVIEW. There are fewer noticeable differences
|
||||
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||
}
|
||||
|
||||
editor.ij.virtualFile?.let { file ->
|
||||
masks.split(";").forEach { mask ->
|
||||
val trimmed = mask.trim()
|
||||
if (trimmed.isNotEmpty() && PatternUtil.fromMask(trimmed).matcher(file.name).matches()) {
|
||||
return true
|
||||
val sourceKind = editorKindToSoftWrapAppliancesPlace(sourceEditor.ij.editorKind)
|
||||
val targetKind = editorKindToSoftWrapAppliancesPlace(targetEditor.ij.editorKind)
|
||||
return sourceKind == targetKind
|
||||
}
|
||||
|
||||
|
||||
private fun getGlobalIsUseSoftWraps(editor: VimEditor): Boolean {
|
||||
val softWrapAppliancePlace = when (editor.ij.editorKind) {
|
||||
EditorKind.UNTYPED,
|
||||
EditorKind.DIFF,
|
||||
EditorKind.MAIN_EDITOR -> SoftWrapAppliancePlaces.MAIN_EDITOR
|
||||
EditorKind.CONSOLE -> SoftWrapAppliancePlaces.CONSOLE
|
||||
EditorKind.PREVIEW -> SoftWrapAppliancePlaces.PREVIEW
|
||||
}
|
||||
|
||||
val settings = EditorSettingsExternalizable.getInstance()
|
||||
if (softWrapAppliancePlace == SoftWrapAppliancePlaces.MAIN_EDITOR) {
|
||||
if (settings.isUseSoftWraps) {
|
||||
val masks = settings.softWrapFileMasks
|
||||
if (masks.trim() == "*") return true
|
||||
|
||||
editor.ij.virtualFile?.let { file ->
|
||||
masks.split(";").forEach { mask ->
|
||||
val trimmed = mask.trim()
|
||||
if (trimmed.isNotEmpty() && PatternUtil.fromMask(trimmed).matcher(file.name).matches()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
return settings.isUseSoftWraps(softWrapAppliancePlace)
|
||||
}
|
||||
|
||||
private fun getEffectiveIsUseSoftWraps(editor: VimEditor) = editor.ij.settings.isUseSoftWraps
|
||||
@@ -1241,28 +1313,28 @@ private class WrapOptionMapper(wrapOption: ToggleOption, internalOptionValueAcce
|
||||
}
|
||||
|
||||
|
||||
public class IjOptionConstants {
|
||||
class IjOptionConstants {
|
||||
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName")
|
||||
public companion object {
|
||||
companion object {
|
||||
|
||||
public const val idearefactormode_keep: String = "keep"
|
||||
public const val idearefactormode_select: String = "select"
|
||||
public const val idearefactormode_visual: String = "visual"
|
||||
const val idearefactormode_keep: String = "keep"
|
||||
const val idearefactormode_select: String = "select"
|
||||
const val idearefactormode_visual: String = "visual"
|
||||
|
||||
public const val ideastatusicon_enabled: String = "enabled"
|
||||
public const val ideastatusicon_gray: String = "gray"
|
||||
public const val ideastatusicon_disabled: String = "disabled"
|
||||
const val ideastatusicon_enabled: String = "enabled"
|
||||
const val ideastatusicon_gray: String = "gray"
|
||||
const val ideastatusicon_disabled: String = "disabled"
|
||||
|
||||
public const val ideavimsupport_dialog: String = "dialog"
|
||||
public const val ideavimsupport_singleline: String = "singleline"
|
||||
public const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
|
||||
const val ideavimsupport_dialog: String = "dialog"
|
||||
const val ideavimsupport_singleline: String = "singleline"
|
||||
const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
|
||||
|
||||
public const val ideawrite_all: String = "all"
|
||||
public const val ideawrite_file: String = "file"
|
||||
const val ideawrite_all: String = "all"
|
||||
const val ideawrite_file: String = "file"
|
||||
|
||||
public val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
||||
public val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
||||
public val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
|
||||
public val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
||||
val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
||||
val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
||||
val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
|
||||
val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
||||
}
|
||||
}
|
||||
|
@@ -18,111 +18,22 @@ import com.intellij.openapi.progress.ProgressIndicatorProvider
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.util.execution.ParametersListUtil
|
||||
import com.intellij.util.text.CharSequenceReader
|
||||
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.KeyProcessResult
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimProcessGroupBase
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.helper.requestFocus
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
|
||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.returnTo
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import java.io.BufferedWriter
|
||||
import java.io.IOException
|
||||
import java.io.OutputStreamWriter
|
||||
import java.io.Reader
|
||||
import java.io.Writer
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
public class ProcessGroup : VimProcessGroupBase() {
|
||||
override var lastCommand: String? = null
|
||||
private set
|
||||
override var isCommandProcessing: Boolean = false
|
||||
override var modeBeforeCommandProcessing: Mode? = null
|
||||
|
||||
public override fun startExEntry(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
command: Command,
|
||||
label: String,
|
||||
initialCommandText: String,
|
||||
) {
|
||||
// Don't allow ex commands in one line editors
|
||||
if (editor.isOneLineMode()) return
|
||||
|
||||
val currentMode = editor.vimStateMachine.mode
|
||||
check(currentMode is ReturnableFromCmd) {
|
||||
"Cannot enable cmd mode from current mode $currentMode"
|
||||
}
|
||||
|
||||
isCommandProcessing = true
|
||||
modeBeforeCommandProcessing = currentMode
|
||||
|
||||
// Make sure the Visual selection marks are up to date before we use them.
|
||||
injector.markService.setVisualSelectionMarks(editor)
|
||||
|
||||
val rangeText = getRange(editor, command)
|
||||
|
||||
// Note that we should remove selection and reset caret offset before we switch back to Normal mode and then enter
|
||||
// Command-line mode. However, some IdeaVim commands can handle multiple carets, including multiple carets with
|
||||
// selection (which might or might not be a block selection). Unfortunately, because we're just entering
|
||||
// Command-line mode, we don't know which command is going to be entered, so we can't remove selection here.
|
||||
// Therefore, we switch to Normal and then Command-line even though we might still have a Visual selection...
|
||||
// On the plus side, it means we still show selection while editing the command line, which Vim also does
|
||||
// (Normal then Command-line is not strictly necessary, but done for completeness and autocmd)
|
||||
// Caret selection is finally handled in Command.execute
|
||||
editor.mode = Mode.NORMAL()
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
|
||||
injector.commandLine.create(editor, context, ":", rangeText + initialCommandText, 1)
|
||||
}
|
||||
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
||||
// is open. So I'll put focus back in the editor and process the key.
|
||||
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
if (panel.isActive) {
|
||||
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||
lambdaEditor.mode = Mode.NORMAL()
|
||||
getInstance().reset(lambdaEditor)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
|
||||
// If 'cpoptions' contains 'x', then Escape should execute the command line. This is the default for Vi but not Vim.
|
||||
// IdeaVim does not (currently?) support 'cpoptions', so sticks with Vim's default behaviour. Escape cancels.
|
||||
editor.mode = editor.mode.returnTo()
|
||||
getInstance().reset(editor)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.deactivate(true, resetCaret)
|
||||
}
|
||||
|
||||
private fun getRange(editor: VimEditor, cmd: Command) = when {
|
||||
editor.inVisualMode -> "'<,'>"
|
||||
cmd.rawCount == 1 -> "."
|
||||
cmd.rawCount > 1 -> ".,.+" + (cmd.count - 1)
|
||||
else -> ""
|
||||
}
|
||||
|
||||
class ProcessGroup : VimProcessGroupBase() {
|
||||
@Throws(ExecutionException::class, ProcessCanceledException::class)
|
||||
public override fun executeCommand(
|
||||
override fun executeCommand(
|
||||
editor: VimEditor,
|
||||
command: String,
|
||||
input: CharSequence?,
|
||||
@@ -184,8 +95,6 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
val progressIndicator = ProgressIndicatorProvider.getInstance().progressIndicator
|
||||
val output = handler.runProcessWithProgressIndicator(progressIndicator)
|
||||
|
||||
lastCommand = command
|
||||
|
||||
if (output.isCancelled) {
|
||||
// TODO: Vim will use whatever text has already been written to stdout
|
||||
// For whatever reason, we're not getting any here, so just throw an exception
|
||||
@@ -221,7 +130,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
companion object {
|
||||
private val logger = logger<ProcessGroup>()
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@ import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
||||
import com.maddyhome.idea.vim.register.Register;
|
||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||
@@ -35,6 +37,10 @@ import java.util.List;
|
||||
})
|
||||
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
|
||||
|
||||
static {
|
||||
IjVimInjectorKt.initInjector();
|
||||
}
|
||||
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
|
||||
|
||||
public RegisterGroup() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.mark.Jump
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.initInjector
|
||||
import org.jdom.Element
|
||||
|
||||
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
|
||||
@@ -53,6 +54,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
||||
jumpElem.setAttribute("line", jump.line.toString())
|
||||
jumpElem.setAttribute("column", jump.col.toString())
|
||||
jumpElem.setAttribute("filename", StringUtil.notNullize(jump.filepath))
|
||||
jumpElem.setAttribute("protocol", StringUtil.notNullize(jump.protocol))
|
||||
projectElement.addContent(jumpElem)
|
||||
if (logger.isDebug()) {
|
||||
logger.debug("saved jump = $jump")
|
||||
@@ -64,6 +66,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
||||
}
|
||||
|
||||
override fun loadState(state: Element) {
|
||||
initInjector()
|
||||
val projectElements = state.getChildren("project")
|
||||
for (projectElement in projectElements) {
|
||||
val jumps = mutableListOf<Jump>()
|
||||
@@ -73,6 +76,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
||||
Integer.parseInt(jumpElement.getAttributeValue("line")),
|
||||
Integer.parseInt(jumpElement.getAttributeValue("column")),
|
||||
jumpElement.getAttributeValue("filename"),
|
||||
jumpElement.getAttributeValue("protocol", "file"),
|
||||
)
|
||||
jumps.add(jump)
|
||||
}
|
||||
@@ -87,6 +91,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
||||
|
||||
internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
|
||||
initInjector()
|
||||
if (!injector.globalIjOptions().unifyjumps) return
|
||||
|
||||
val jumpService = injector.jumpService
|
||||
@@ -94,7 +99,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||
// we do not want jumps that were processed before
|
||||
val jump = buildJump(changePlace) ?: return
|
||||
jumpService.addJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump, true)
|
||||
jumpService.addJump(injector.file.getProjectId(project), jump, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
if (changePlace.timeStamp < jumpService.lastJumpTimeStamp) return // this listener is notified asynchronously, and
|
||||
// we do not want jumps that were processed before
|
||||
val jump = buildJump(changePlace) ?: return
|
||||
jumpService.removeJump(project.basePath ?: IjVimEditor.DEFAULT_PROJECT_ID, jump)
|
||||
jumpService.removeJump(injector.file.getProjectId(project), jump)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +125,6 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
|
||||
val path = place.file.path
|
||||
|
||||
return Jump(line, col, path)
|
||||
return Jump(line, col, path, place.file.fileSystem.protocol)
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@ import com.maddyhome.idea.vim.put.PutData
|
||||
import com.maddyhome.idea.vim.put.VimPasteProvider
|
||||
import com.maddyhome.idea.vim.put.VimPutBase
|
||||
import com.maddyhome.idea.vim.register.RegisterConstants
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.isBlock
|
||||
import com.maddyhome.idea.vim.state.mode.isChar
|
||||
@@ -83,6 +84,11 @@ internal class PutGroup : VimPutBase() {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val context = vimContext.context as DataContext
|
||||
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
||||
if (injector.vimState.mode is Mode.INSERT) {
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
||||
}
|
||||
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
|
||||
val startOffset =
|
||||
prepareDocumentAndGetStartOffsets(
|
||||
|
@@ -53,12 +53,12 @@ import javax.swing.Timer
|
||||
* no adjustment gets performed and IdeaVim stays in insert mode.
|
||||
*/
|
||||
// Do not remove until it's used in EasyMotion plugin in tests
|
||||
public object VimVisualTimer {
|
||||
object VimVisualTimer {
|
||||
|
||||
public var swingTimer: Timer? = null
|
||||
public var mode: Mode? = null
|
||||
var swingTimer: Timer? = null
|
||||
var mode: Mode? = null
|
||||
|
||||
public inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
|
||||
inline fun singleTask(currentMode: Mode, crossinline task: (initialMode: Mode?) -> Unit) {
|
||||
swingTimer?.stop()
|
||||
|
||||
if (mode == null) mode = currentMode
|
||||
@@ -70,7 +70,7 @@ public object VimVisualTimer {
|
||||
swingTimer = timer
|
||||
}
|
||||
|
||||
public fun doNow() {
|
||||
fun doNow() {
|
||||
val swingTimer1 = swingTimer
|
||||
if (swingTimer1 != null) {
|
||||
swingTimer1.stop()
|
||||
@@ -80,12 +80,12 @@ public object VimVisualTimer {
|
||||
}
|
||||
}
|
||||
|
||||
public fun drop() {
|
||||
fun drop() {
|
||||
swingTimer?.stop()
|
||||
swingTimer = null
|
||||
}
|
||||
|
||||
public inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
|
||||
inline fun timerAction(task: (initialMode: Mode?) -> Unit) {
|
||||
task(mode)
|
||||
swingTimer = null
|
||||
mode = null
|
||||
|
@@ -9,13 +9,9 @@
|
||||
package com.maddyhome.idea.vim.group.visual
|
||||
|
||||
import com.intellij.find.FindManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimVisualMotionGroupBase
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.command.engine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
/**
|
||||
@@ -31,12 +27,4 @@ internal class VisualMotionGroup : VimVisualMotionGroupBase() {
|
||||
|
||||
return super.autodetectVisualSubmode(editor)
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Added a method
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
fun enterVisualMode(editor: Editor, subMode: CommandState.SubMode? = null): Boolean {
|
||||
return this.enterVisualMode(editor.vim, subMode?.engine)
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.maddyhome.idea.vim.api.key
|
||||
import com.maddyhome.idea.vim.newapi.initInjector
|
||||
|
||||
/**
|
||||
* Logs the chain of handlers for esc and enter
|
||||
@@ -34,6 +35,8 @@ internal class EditorHandlersChainLogger : ProjectActivity {
|
||||
private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
|
||||
|
||||
override suspend fun execute(project: Project) {
|
||||
initInjector()
|
||||
|
||||
if (!enableOctopus) return
|
||||
|
||||
val escHandlers = editorHandlers.extensionList
|
||||
|
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
*/
|
||||
internal abstract class IdeActionHandler(private val actionName: String) : VimActionHandler.SingleExecution() {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||
injector.actionExecutor.executeAction(actionName, context)
|
||||
injector.actionExecutor.executeAction(editor, name = actionName, context = context)
|
||||
injector.scroll.scrollCaretIntoView(editor)
|
||||
return true
|
||||
}
|
||||
|
@@ -218,13 +218,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle
|
||||
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
|
||||
override val key: String = "<Esc>"
|
||||
|
||||
private val ideaVimSupportDialog
|
||||
get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||
|
||||
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
|
||||
val ideaVimSupportDialog =
|
||||
injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||
|
||||
return editor.isPrimaryEditor() ||
|
||||
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
|
||||
ideaVimSupportDialog && !editor.vim.mode.inNormalMode
|
||||
EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) ||
|
||||
ideaVimSupportDialog && vimStateNeedsToHandleEscape(editor)
|
||||
}
|
||||
|
||||
private fun vimStateNeedsToHandleEscape(editor: Editor): Boolean {
|
||||
return !editor.vim.mode.inNormalMode || KeyHandler.getInstance().keyHandlerState.mappingState.hasKeys
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.EditorListener
|
||||
import com.maddyhome.idea.vim.common.IsReplaceCharListener
|
||||
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
@@ -73,7 +74,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
|
||||
}
|
||||
|
||||
private fun Editor.guicursorMode(): GuiCursorMode {
|
||||
return GuiCursorMode.fromMode(vim.mode, vim.vimStateMachine.isReplaceCharacter)
|
||||
return GuiCursorMode.fromMode(vim.mode, injector.vimState.isReplaceCharacter)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +87,7 @@ private fun isBlockCursorOverride() = EditorSettingsExternalizable.getInstance()
|
||||
|
||||
private fun Editor.updatePrimaryCaretVisualAttributes() {
|
||||
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
|
||||
if (isIdeaVimDisabledHere) return
|
||||
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
|
||||
|
||||
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
||||
@@ -99,6 +101,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
|
||||
|
||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
|
||||
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
|
||||
if (isIdeaVimDisabledHere) return
|
||||
// IntelliJ simulates visual block with multiple carets with selections. Do our best to hide them
|
||||
val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
|
||||
this.caretModel.allCarets.forEach {
|
||||
@@ -144,7 +147,7 @@ private object AttributesCache {
|
||||
@TestOnly
|
||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
||||
|
||||
public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
|
||||
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener, EditorListener {
|
||||
override fun isReplaceCharChanged(editor: VimEditor) {
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
@@ -153,21 +156,19 @@ public class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeLi
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
|
||||
private fun updateCaretsVisual(editor: VimEditor) {
|
||||
if (injector.globalOptions().ideaglobalmode) {
|
||||
updateAllEditorsCaretsVisual()
|
||||
} else {
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
ijEditor.updateCaretsVisualAttributes()
|
||||
ijEditor.updateCaretsVisualPosition()
|
||||
}
|
||||
override fun focusGained(editor: VimEditor) {
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
|
||||
public fun updateAllEditorsCaretsVisual() {
|
||||
private fun updateCaretsVisual(editor: VimEditor) {
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
ijEditor.updateCaretsVisualAttributes()
|
||||
ijEditor.updateCaretsVisualPosition()
|
||||
}
|
||||
|
||||
fun updateAllEditorsCaretsVisual() {
|
||||
injector.editorGroup.getEditors().forEach { editor ->
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
ijEditor.updateCaretsVisualAttributes()
|
||||
ijEditor.updateCaretsVisualPosition()
|
||||
updateCaretsVisual(editor)
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,13 +11,7 @@
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.api.Options
|
||||
import com.maddyhome.idea.vim.api.hasValue
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
import com.maddyhome.idea.vim.options.OptionConstants
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||
|
||||
@@ -27,57 +21,17 @@ internal val Mode.hasVisualSelection
|
||||
else -> false
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: New method
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
public val Editor.mode: CommandState.Mode
|
||||
get() {
|
||||
val mode = this.vim.vimStateMachine.mode
|
||||
return when (mode) {
|
||||
is Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
|
||||
Mode.INSERT -> CommandState.Mode.INSERT
|
||||
is Mode.NORMAL -> CommandState.Mode.COMMAND
|
||||
is Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
|
||||
Mode.REPLACE -> CommandState.Mode.REPLACE
|
||||
is Mode.SELECT -> CommandState.Mode.SELECT
|
||||
is Mode.VISUAL -> CommandState.Mode.VISUAL
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: New method
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
@Deprecated("Please migrate to VimEditor.isEndAllowed which can correctly access virtualedit at the right scope",
|
||||
replaceWith = ReplaceWith("VimEditor.isEndAllowed"))
|
||||
public val CommandState.Mode.isEndAllowed: Boolean
|
||||
get() {
|
||||
fun possiblyUsesVirtualSpace(): Boolean {
|
||||
// virtualedit is GLOBAL_OR_LOCAL_TO_WINDOW. We should be using EFFECTIVE, but we don't have a valid editor (which
|
||||
// is why this property is deprecated). Fetch the global value, passing in the fallback window to avoid asserts
|
||||
// DO NOT COPY THIS APPROACH - ALWAYS USE A REAL WINDOW FOR NON-GLOBAL OPTIONS!
|
||||
return injector.optionGroup.hasValue(Options.virtualedit, OptionAccessScope.GLOBAL(injector.fallbackWindow), OptionConstants.virtualedit_onemore)
|
||||
}
|
||||
|
||||
return when (this) {
|
||||
CommandState.Mode.INSERT, CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true
|
||||
CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> possiblyUsesVirtualSpace()
|
||||
CommandState.Mode.INSERT_NORMAL, CommandState.Mode.INSERT_VISUAL, CommandState.Mode.INSERT_SELECT -> possiblyUsesVirtualSpace()
|
||||
}
|
||||
}
|
||||
|
||||
public val Mode.inNormalMode: Boolean
|
||||
val Mode.inNormalMode: Boolean
|
||||
get() = this is Mode.NORMAL
|
||||
|
||||
@get:JvmName("inInsertMode")
|
||||
public val Editor.inInsertMode: Boolean
|
||||
val Editor.inInsertMode: Boolean
|
||||
get() = this.vim.mode == Mode.INSERT || this.vim.mode == Mode.REPLACE
|
||||
|
||||
@get:JvmName("inVisualMode")
|
||||
public val Editor.inVisualMode: Boolean
|
||||
val Editor.inVisualMode: Boolean
|
||||
get() = this.vim.inVisualMode
|
||||
|
||||
@get:JvmName("inExMode")
|
||||
internal val Editor.inExMode
|
||||
get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE
|
||||
get() = this.vim.mode is Mode.CMD_LINE
|
||||
|
@@ -100,15 +100,6 @@ public class EditorHelper {
|
||||
return EngineEditorHelperKt.normalizeVisualLine(new IjVimEditor(editor), line);
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Created a function
|
||||
* Please see: <a href="https://jb.gg/zo8n0r">doc</a>
|
||||
*/
|
||||
public static int getVisualLineCount(final @NotNull Editor editor) {
|
||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
||||
return EngineEditorHelperKt.getVisualLineCount(editor1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Best efforts to ensure that scroll offset doesn't overlap itself.
|
||||
* <p>
|
||||
|
@@ -28,7 +28,7 @@ import javax.swing.JComponent
|
||||
import javax.swing.JTable
|
||||
|
||||
@Deprecated("Use fileSize from VimEditor")
|
||||
public val Editor.fileSize: Int
|
||||
val Editor.fileSize: Int
|
||||
get() = document.textLength
|
||||
|
||||
/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user