mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-21 07:54:06 +02:00
Compare commits
424 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
f33589fbce
|
|||
35445cb730
|
|||
9cba953bf7
|
|||
eb7c1953ae
|
|||
8d4a1cd7d5
|
|||
fd33e4254b
|
|||
f242cf18d7
|
|||
ac46a45f26
|
|||
9d1e29d4bc
|
|||
8c30f802ca
|
|||
766f3f498d
|
|||
60d9c59a3e
|
|||
025bfe5aba
|
|||
2edb650830
|
|||
3d75e14dc4
|
|||
db0290d218
|
|||
36716fe849
|
|||
7cfe42688b
|
|||
1d9fb7f6a7
|
|||
8c4828a6fd
|
|||
86f11a9554
|
|||
![]() |
4614e2ad54 | ||
![]() |
077de91e01 | ||
![]() |
1ce7a97f2a | ||
![]() |
02a42843a6 | ||
![]() |
eb72762073 | ||
![]() |
43a94b3d71 | ||
![]() |
1984873b1c | ||
![]() |
4962baabad | ||
![]() |
2e550a0960 | ||
![]() |
bc48a45cd1 | ||
![]() |
a005eb0612 | ||
![]() |
63297e685c | ||
![]() |
6c9a5e1f2d | ||
![]() |
34a5ba0ba7 | ||
![]() |
2354dc9af9 | ||
![]() |
be31963a4f | ||
![]() |
277c5ae7a5 | ||
![]() |
49d03cf7f1 | ||
![]() |
f5e7459b37 | ||
![]() |
df8144fc00 | ||
![]() |
05f1d7abd7 | ||
![]() |
6ca17ae09d | ||
![]() |
f9d8b3a59e | ||
![]() |
3279325694 | ||
![]() |
b2a6d0d5c7 | ||
![]() |
e32fa6dd11 | ||
![]() |
ec3db81c6d | ||
![]() |
7062dc4860 | ||
![]() |
46619040b1 | ||
![]() |
035804860c | ||
![]() |
0f1a4d523f | ||
![]() |
5f5a97a7e1 | ||
![]() |
03996dce59 | ||
![]() |
0dd63c7b57 | ||
![]() |
94d7902ef2 | ||
![]() |
3d08170d54 | ||
![]() |
cccb23d9ee | ||
![]() |
eae3fb3ebe | ||
![]() |
7f5dce4051 | ||
![]() |
25e3988dcf | ||
![]() |
36589c5250 | ||
![]() |
f1f86b5cd2 | ||
![]() |
628b3ca89f | ||
![]() |
d2b929ddd0 | ||
![]() |
43d770ff5a | ||
![]() |
cde9bc94e6 | ||
![]() |
63f6e73223 | ||
![]() |
ad28e09fec | ||
![]() |
f616f2c3c1 | ||
![]() |
66aec26091 | ||
![]() |
1d59c49b95 | ||
![]() |
8da15b8c08 | ||
![]() |
e22c2b6473 | ||
![]() |
e293c1b786 | ||
![]() |
468ca840ed | ||
![]() |
c75e6756c0 | ||
![]() |
52737c60cf | ||
![]() |
e99c191d49 | ||
![]() |
5db2984fdd | ||
![]() |
365b58eb56 | ||
![]() |
b026144254 | ||
![]() |
0e3cda827c | ||
![]() |
ed2fe3dcf0 | ||
![]() |
791edbd29b | ||
![]() |
4f9d76ef66 | ||
![]() |
4b7381901d | ||
![]() |
6e2cb9ba11 | ||
![]() |
91cd4ab01f | ||
![]() |
34d23180bd | ||
![]() |
fc5aaa50d8 | ||
![]() |
c7bbfdcaf5 | ||
![]() |
562906fe6d | ||
![]() |
976771d11a | ||
![]() |
5fc4462b03 | ||
![]() |
5f263e7014 | ||
![]() |
4c899fcc93 | ||
![]() |
2f8fe392af | ||
![]() |
84c7e1159b | ||
![]() |
18d6f79796 | ||
![]() |
a745da9761 | ||
![]() |
5eb36ce428 | ||
![]() |
b0951f4a38 | ||
![]() |
bcf519027e | ||
![]() |
9aece44a00 | ||
![]() |
61912e2c9b | ||
![]() |
92ee271f1e | ||
![]() |
babc5daf3b | ||
![]() |
c0a10c65e1 | ||
![]() |
5145bb4193 | ||
![]() |
8825f790d4 | ||
![]() |
a0f0f71b6a | ||
![]() |
6a73f9e65a | ||
![]() |
4ee858877d | ||
![]() |
82d18cfbb9 | ||
![]() |
b0dd75f77c | ||
![]() |
727cfb36ba | ||
![]() |
a7d5c5f2d4 | ||
![]() |
1e02848a66 | ||
![]() |
9d8afc5fcb | ||
![]() |
15b2a17ae6 | ||
![]() |
39a2cdcf50 | ||
![]() |
2fd488531b | ||
![]() |
e69b30796c | ||
![]() |
790a0afdf3 | ||
![]() |
66b01b0b0d | ||
![]() |
71ea6184a0 | ||
![]() |
0b1c79d961 | ||
![]() |
6b751fae6e | ||
![]() |
152066b73c | ||
![]() |
dfe645c4eb | ||
![]() |
5024fd94da | ||
![]() |
4d3a231a35 | ||
![]() |
e1a803d310 | ||
![]() |
96b224c0d8 | ||
![]() |
8b6de3f5c6 | ||
![]() |
9014d99cde | ||
![]() |
8dfd41a891 | ||
![]() |
7e62e816a5 | ||
![]() |
9c4965581a | ||
![]() |
d1abc0c9f9 | ||
![]() |
43555ad581 | ||
![]() |
640e5ac552 | ||
![]() |
40b706bcf4 | ||
![]() |
5b355a8e95 | ||
![]() |
8ed53284e4 | ||
![]() |
1f6e124f9d | ||
![]() |
5198864f34 | ||
![]() |
ecfff61aad | ||
![]() |
26909af5de | ||
![]() |
ad762379fb | ||
![]() |
dd4f2de368 | ||
![]() |
879d191800 | ||
![]() |
9d9e38843d | ||
![]() |
f7aded99e8 | ||
![]() |
4e1de61d77 | ||
![]() |
34fe09c8f9 | ||
![]() |
10283ce2f8 | ||
![]() |
cc53b59658 | ||
![]() |
c758a79f79 | ||
![]() |
01d00d45d8 | ||
![]() |
5c7edc498f | ||
![]() |
9c403d2862 | ||
![]() |
ebbd733bba | ||
![]() |
88d1e1d24a | ||
![]() |
c19f2aeee4 | ||
![]() |
86202c846a | ||
![]() |
9a7ff442f3 | ||
![]() |
3752a97d74 | ||
![]() |
5572dfc80d | ||
![]() |
100f420d46 | ||
![]() |
3fcc4746d8 | ||
![]() |
5c5ac192da | ||
![]() |
a0a2163ba0 | ||
![]() |
02724cadce | ||
![]() |
b205f87902 | ||
![]() |
314304246a | ||
![]() |
785688b1ca | ||
![]() |
4c09ab4766 | ||
![]() |
ee447bce07 | ||
![]() |
5fb4c10f88 | ||
![]() |
ed2fcb08b0 | ||
dedd90ce13 | |||
![]() |
73326e623e | ||
![]() |
a2bc34d6ec | ||
![]() |
b652c7726a | ||
![]() |
fb7a2de07b | ||
![]() |
def9ca479b | ||
![]() |
0936e0761f | ||
![]() |
09a335bcfe | ||
![]() |
37b8d69bac | ||
![]() |
13308050a8 | ||
![]() |
a1a553ebc9 | ||
![]() |
5bb0c4f7cb | ||
![]() |
da6736f24a | ||
![]() |
4df1ce2ae8 | ||
![]() |
00fd4cd491 | ||
![]() |
d185672e2f | ||
![]() |
55fef03a4a | ||
![]() |
69b3e4f782 | ||
![]() |
6be29b0378 | ||
![]() |
9965c863a6 | ||
![]() |
3f11ae512c | ||
![]() |
1c842eb3d8 | ||
![]() |
c7fbce675b | ||
![]() |
d7e68488c8 | ||
![]() |
69d13a74e6 | ||
![]() |
5a83df34a7 | ||
![]() |
0a18c388e0 | ||
![]() |
1a3409e7df | ||
![]() |
e93db961a0 | ||
![]() |
8fd76bd08f | ||
![]() |
0eea4a5b2c | ||
![]() |
18a0c533e2 | ||
![]() |
0bd8d8f4d2 | ||
![]() |
64a89c8863 | ||
![]() |
5b17d7740e | ||
![]() |
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 |
3
.github/workflows/closeYoutrackOnCommit.yml
vendored
3
.github/workflows/closeYoutrackOnCommit.yml
vendored
@@ -8,6 +8,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
|
5
.github/workflows/codeql-analysis.yml
vendored
5
.github/workflows/codeql-analysis.yml
vendored
@@ -20,6 +20,11 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '44 12 * * 4'
|
- cron: '44 12 * * 4'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
|
2
.github/workflows/integrationsTest.yml
vendored
2
.github/workflows/integrationsTest.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
|||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: ./gradlew integrationsTest
|
run: ./gradlew --no-configuration-cache integrationsTest
|
||||||
env:
|
env:
|
||||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
||||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
10
.github/workflows/runUiOctopusTests.yml
vendored
10
.github/workflows/runUiOctopusTests.yml
vendored
@@ -16,14 +16,14 @@ jobs:
|
|||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
- name: Setup Gradle
|
# - name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
# uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
run: gradle :buildPlugin
|
run: gradle :buildPlugin
|
||||||
- name: Run Idea
|
- name: Run Idea
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/reports
|
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
|
- name: Wait for Idea started
|
||||||
uses: jtalk/url-health-check-action@v3
|
uses: jtalk/url-health-check-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
run: mv tests/ui-ij-tests/video build/reports
|
run: mv tests/ui-ij-tests/video build/reports
|
||||||
- name: Move sandbox logs
|
- name: Move sandbox logs
|
||||||
if: always()
|
if: always()
|
||||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
build/reports
|
build/reports
|
||||||
tests/ui-ij-tests/build/reports
|
tests/ui-ij-tests/build/reports
|
||||||
sandbox-idea-log
|
idea-sandbox-log
|
||||||
# build-for-ui-test-linux:
|
# build-for-ui-test-linux:
|
||||||
# runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
# steps:
|
# steps:
|
||||||
|
10
.github/workflows/runUiPyTests.yml
vendored
10
.github/workflows/runUiPyTests.yml
vendored
@@ -19,14 +19,14 @@ jobs:
|
|||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
- name: Setup Gradle
|
# - name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
# uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
run: gradle :buildPlugin
|
run: gradle :buildPlugin
|
||||||
- name: Run Idea
|
- name: Run Idea
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/reports
|
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
|
- name: Wait for Idea started
|
||||||
uses: jtalk/url-health-check-action@v3
|
uses: jtalk/url-health-check-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
run: mv tests/ui-py-tests/video build/reports
|
run: mv tests/ui-py-tests/video build/reports
|
||||||
- name: Move sandbox logs
|
- name: Move sandbox logs
|
||||||
if: always()
|
if: always()
|
||||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
run: mv build/idea-sandbox/PC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -49,4 +49,4 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
build/reports
|
build/reports
|
||||||
tests/ui-py-tests/build/reports
|
tests/ui-py-tests/build/reports
|
||||||
sandbox-idea-log
|
idea-sandbox-log
|
||||||
|
10
.github/workflows/runUiTests.yml
vendored
10
.github/workflows/runUiTests.yml
vendored
@@ -16,14 +16,14 @@ jobs:
|
|||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup FFmpeg
|
- name: Setup FFmpeg
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
- name: Setup Gradle
|
# - name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.4.2
|
# uses: gradle/gradle-build-action@v2.4.2
|
||||||
- name: Build Plugin
|
- name: Build Plugin
|
||||||
run: gradle :buildPlugin
|
run: gradle :buildPlugin
|
||||||
- name: Run Idea
|
- name: Run Idea
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build/reports
|
mkdir -p build/reports
|
||||||
gradle runIdeForUiTests > build/reports/idea.log &
|
gradle --no-configuration-cache runIdeForUiTests > build/reports/idea.log &
|
||||||
- name: Wait for Idea started
|
- name: Wait for Idea started
|
||||||
uses: jtalk/url-health-check-action@v3
|
uses: jtalk/url-health-check-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
run: mv tests/ui-ij-tests/video build/reports
|
run: mv tests/ui-ij-tests/video build/reports
|
||||||
- name: Move sandbox logs
|
- name: Move sandbox logs
|
||||||
if: always()
|
if: always()
|
||||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
run: mv build/idea-sandbox/IC-2024.1.2/log_runIdeForUiTests idea-sandbox-log
|
||||||
- name: Save report
|
- name: Save report
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
build/reports
|
build/reports
|
||||||
tests/ui-ij-tests/build/reports
|
tests/ui-ij-tests/build/reports
|
||||||
sandbox-idea-log
|
idea-sandbox-log
|
||||||
# build-for-ui-test-linux:
|
# build-for-ui-test-linux:
|
||||||
# runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
# steps:
|
# steps:
|
||||||
|
14
.github/workflows/syncDoc.yml
vendored
14
.github/workflows/syncDoc.yml
vendored
@@ -10,6 +10,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
@@ -34,6 +37,17 @@ jobs:
|
|||||||
id: update_authors
|
id: update_authors
|
||||||
run: cp -a origin/doc/. docs
|
run: cp -a origin/doc/. docs
|
||||||
|
|
||||||
|
# The Wiki for github should have no `.md` in references
|
||||||
|
# Otherwise, such links will lead to the raw text.
|
||||||
|
# However, the `.md` should exist to have a navigation in GitHub code.
|
||||||
|
- name: Replace `.md)` with `)`
|
||||||
|
run: |
|
||||||
|
# Define the directory you want to process
|
||||||
|
DIRECTORY="docs"
|
||||||
|
|
||||||
|
# Find all files in the directory and perform the replacement
|
||||||
|
find $DIRECTORY -type f -exec sed -i 's/\.md)/)/g' {} +
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: stefanzweifel/git-auto-commit-action@v4
|
uses: stefanzweifel/git-auto-commit-action@v4
|
||||||
with:
|
with:
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
*.swp
|
*.swp
|
||||||
/.gradle/
|
/.gradle/
|
||||||
|
/.intellijPlatform/
|
||||||
|
|
||||||
/.idea/
|
/.idea/
|
||||||
!/.idea/scopes
|
!/.idea/scopes
|
||||||
@@ -26,9 +27,8 @@
|
|||||||
# Generated by gradle task "generateGrammarSource"
|
# Generated by gradle task "generateGrammarSource"
|
||||||
vim-engine/src/main/java/com/maddyhome/idea/vim/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
|
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||||
# Generated JSONs for lazy classloading
|
|
||||||
/vim-engine/src/main/resources/ksp-generated
|
|
||||||
/src/main/resources/ksp-generated
|
|
||||||
|
|
||||||
# Created by github automation
|
# Created by github automation
|
||||||
settings.xml
|
settings.xml
|
||||||
|
|
||||||
|
.kotlin
|
@@ -12,7 +12,7 @@
|
|||||||
<option name="taskNames">
|
<option name="taskNames">
|
||||||
<list>
|
<list>
|
||||||
<option value="check" />
|
<option value="check" />
|
||||||
<option value="runPluginVerifier" />
|
<option value="verifyPlugin" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="vmOptions" value="" />
|
<option name="vmOptions" value="" />
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
<DebugAllEnabled>false</DebugAllEnabled>
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>false</RunAsTest>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
15
.teamcity/_Self/Constants.kt
vendored
15
.teamcity/_Self/Constants.kt
vendored
@@ -5,13 +5,12 @@ object Constants {
|
|||||||
const val EAP_CHANNEL = "eap"
|
const val EAP_CHANNEL = "eap"
|
||||||
const val DEV_CHANNEL = "Dev"
|
const val DEV_CHANNEL = "Dev"
|
||||||
|
|
||||||
const val GITHUB_TESTS = "2024.1.1"
|
const val NVIM_TESTS = "2024.2.1"
|
||||||
const val NVIM_TESTS = "2024.1.1"
|
const val PROPERTY_TESTS = "2024.2.1"
|
||||||
const val PROPERTY_TESTS = "2024.1.1"
|
const val LONG_RUNNING_TESTS = "2024.2.1"
|
||||||
const val LONG_RUNNING_TESTS = "2024.1.1"
|
const val QODANA_TESTS = "2024.2.1"
|
||||||
const val QODANA_TESTS = "2024.1.1"
|
const val RELEASE = "2024.2.1"
|
||||||
const val RELEASE = "2024.1.1"
|
|
||||||
|
|
||||||
const val RELEASE_DEV = "2024.1.1"
|
const val RELEASE_DEV = "2024.2.1"
|
||||||
const val RELEASE_EAP = "2024.1.1"
|
const val RELEASE_EAP = "2024.2.1"
|
||||||
}
|
}
|
||||||
|
10
.teamcity/_Self/Project.kt
vendored
10
.teamcity/_Self/Project.kt
vendored
@@ -8,7 +8,6 @@ import _Self.buildTypes.PropertyBased
|
|||||||
import _Self.buildTypes.Qodana
|
import _Self.buildTypes.Qodana
|
||||||
import _Self.buildTypes.TestingBuildType
|
import _Self.buildTypes.TestingBuildType
|
||||||
import _Self.subprojects.GitHub
|
import _Self.subprojects.GitHub
|
||||||
import _Self.subprojects.OldTests
|
|
||||||
import _Self.subprojects.Releases
|
import _Self.subprojects.Releases
|
||||||
import _Self.vcsRoots.GitHubPullRequest
|
import _Self.vcsRoots.GitHubPullRequest
|
||||||
import _Self.vcsRoots.ReleasesVcsRoot
|
import _Self.vcsRoots.ReleasesVcsRoot
|
||||||
@@ -18,7 +17,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
|||||||
object Project : Project({
|
object Project : Project({
|
||||||
description = "Vim engine for JetBrains IDEs"
|
description = "Vim engine for JetBrains IDEs"
|
||||||
|
|
||||||
subProjects(Releases, OldTests, GitHub)
|
subProjects(Releases, GitHub)
|
||||||
|
|
||||||
// VCS roots
|
// VCS roots
|
||||||
vcsRoot(GitHubPullRequest)
|
vcsRoot(GitHubPullRequest)
|
||||||
@@ -26,7 +25,7 @@ object Project : Project({
|
|||||||
|
|
||||||
// Active tests
|
// Active tests
|
||||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
buildType(TestingBuildType("2024.1.1", "<default>"))
|
buildType(TestingBuildType("2024.2.1", "<default>"))
|
||||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||||
|
|
||||||
buildType(PropertyBased)
|
buildType(PropertyBased)
|
||||||
@@ -43,6 +42,9 @@ object Project : Project({
|
|||||||
abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
|
abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
|
||||||
artifactRules = """
|
artifactRules = """
|
||||||
+:build/reports => build/reports
|
+:build/reports => build/reports
|
||||||
|
+:tests/java-tests/build/reports => java-tests/build/reports
|
||||||
|
+:tests/long-running-tests/build/reports => long-running-tests/build/reports
|
||||||
|
+:tests/property-tests/build/reports => property-tests/build/reports
|
||||||
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ abstract class IdeaVimBuildType(init: BuildType.() -> Unit) : BuildType({
|
|||||||
// These requirements define Linux-Medium configuration.
|
// These requirements define Linux-Medium configuration.
|
||||||
// Unfortunately, requirement by name (teamcity.agent.name) doesn't work
|
// Unfortunately, requirement by name (teamcity.agent.name) doesn't work
|
||||||
// IDK the reason for it, but on our agents this property is empty
|
// IDK the reason for it, but on our agents this property is empty
|
||||||
equals("teamcity.agent.hardware.cpuCount", "4")
|
equals("teamcity.agent.hardware.cpuCount", "16")
|
||||||
equals("teamcity.agent.os.family", "Linux")
|
equals("teamcity.agent.os.family", "Linux")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
5
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
@@ -37,9 +37,12 @@ object Compatibility : IdeaVimBuildType({
|
|||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
|
||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
|
||||||
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
|
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
|
||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
|
# java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.copilot' [latest-IU] -team-city
|
||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.github.dankinsoid.multicursor' [latest-IU] -team-city
|
||||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.joshestein.ideavim-quickscope' [latest-IU] -team-city
|
||||||
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.peekaboo' [latest-IU] -team-city
|
||||||
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.switch' [latest-IU] -team-city
|
||||||
|
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}com.julienphalip.ideavim.functiontextobj' [latest-IU] -team-city
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
2
.teamcity/_Self/buildTypes/PluginVerifier.kt
vendored
@@ -22,7 +22,7 @@ object PluginVerifier : IdeaVimBuildType({
|
|||||||
|
|
||||||
steps {
|
steps {
|
||||||
gradle {
|
gradle {
|
||||||
tasks = "clean runPluginVerifier"
|
tasks = "clean verifyPlugin"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ object ReleaseEapFromBranch : IdeaVimBuildType({
|
|||||||
vcs {
|
vcs {
|
||||||
root(ReleasesVcsRoot)
|
root(ReleasesVcsRoot)
|
||||||
branchFilter = """
|
branchFilter = """
|
||||||
+:heads/(releases/*)
|
+:heads/releases/*
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
checkoutMode = CheckoutMode.AUTO
|
checkoutMode = CheckoutMode.AUTO
|
||||||
|
1
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
1
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@@ -144,6 +144,7 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
|||||||
gradle {
|
gradle {
|
||||||
name = "Run Integrations"
|
name = "Run Integrations"
|
||||||
tasks = "releaseActions"
|
tasks = "releaseActions"
|
||||||
|
gradleParams = "--no-configuration-cache"
|
||||||
}
|
}
|
||||||
// gradle {
|
// gradle {
|
||||||
// name = "Slack Notification"
|
// name = "Slack Notification"
|
||||||
|
@@ -39,9 +39,11 @@ open class TestingBuildType(
|
|||||||
|
|
||||||
steps {
|
steps {
|
||||||
gradle {
|
gradle {
|
||||||
|
clearConditions()
|
||||||
tasks = "clean test"
|
tasks = "clean test"
|
||||||
buildFile = ""
|
buildFile = ""
|
||||||
enableStacktrace = true
|
enableStacktrace = true
|
||||||
|
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
.teamcity/_Self/subprojects/GitHub.kt
vendored
2
.teamcity/_Self/subprojects/GitHub.kt
vendored
@@ -1,6 +1,5 @@
|
|||||||
package _Self.subprojects
|
package _Self.subprojects
|
||||||
|
|
||||||
import _Self.Constants
|
|
||||||
import _Self.IdeaVimBuildType
|
import _Self.IdeaVimBuildType
|
||||||
import _Self.vcsRoots.GitHubPullRequest
|
import _Self.vcsRoots.GitHubPullRequest
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
||||||
@@ -25,7 +24,6 @@ class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({
|
|||||||
|
|
||||||
params {
|
params {
|
||||||
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
|
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
|
||||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", Constants.GITHUB_TESTS)
|
|
||||||
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
|
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
.teamcity/_Self/subprojects/OldTests.kt
vendored
25
.teamcity/_Self/subprojects/OldTests.kt
vendored
@@ -1,25 +0,0 @@
|
|||||||
package _Self.subprojects
|
|
||||||
|
|
||||||
import _Self.buildTypes.TestingBuildType
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
|
||||||
|
|
||||||
object OldTests : Project({
|
|
||||||
name = "Old IdeaVim tests"
|
|
||||||
description = "Tests for older versions of IJ"
|
|
||||||
|
|
||||||
buildType(TestingBuildType("IC-2018.1", "181-182", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2018.2", "181-182", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2018.3", "183", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2019.1", "191-193", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2019.2", "191-193", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2019.3", "191-193", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2020.1", "201", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2020.2", "202", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2020.3", "203-212", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2021.1", "203-212", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2021.2.2", "203-212", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2021.3.2", "213-221", javaVersion = "1.8", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2022.2.3", branch = "222", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2023.1", "231-232", javaPlugin = false))
|
|
||||||
buildType(TestingBuildType("IC-2023.2", "231-232", javaPlugin = false))
|
|
||||||
})
|
|
@@ -1,39 +0,0 @@
|
|||||||
package patches.buildTypes
|
|
||||||
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.GradleBuildStep
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
|
||||||
|
|
||||||
/*
|
|
||||||
This patch script was generated by TeamCity on settings change in UI.
|
|
||||||
To apply the patch, change the buildType with id = 'IdeaVimTests_Latest_EAP'
|
|
||||||
accordingly, and delete the patch script.
|
|
||||||
*/
|
|
||||||
changeBuildType(RelativeId("IdeaVimTests_Latest_EAP")) {
|
|
||||||
check(artifactRules == """
|
|
||||||
+:build/reports => build/reports
|
|
||||||
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
|
||||||
""".trimIndent()) {
|
|
||||||
"Unexpected option value: artifactRules = $artifactRules"
|
|
||||||
}
|
|
||||||
artifactRules = """
|
|
||||||
+:build/reports => build/reports
|
|
||||||
+:/mnt/agent/temp/buildTmp/ => /mnt/agent/temp/buildTmp/
|
|
||||||
+:tests/java-tests/build/reports => tests/java-tests/build/reports
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
||||||
expectSteps {
|
|
||||||
gradle {
|
|
||||||
tasks = "clean test"
|
|
||||||
buildFile = ""
|
|
||||||
enableStacktrace = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
update<GradleBuildStep>(0) {
|
|
||||||
clearConditions()
|
|
||||||
jdkHome = "/usr/lib/jvm/java-17-amazon-corretto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +0,0 @@
|
|||||||
package patches.buildTypes
|
|
||||||
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
|
||||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
|
||||||
|
|
||||||
/*
|
|
||||||
This patch script was generated by TeamCity on settings change in UI.
|
|
||||||
To apply the patch, change the buildType with id = 'ReleaseEapFromBranch'
|
|
||||||
accordingly, and delete the patch script.
|
|
||||||
*/
|
|
||||||
changeBuildType(RelativeId("ReleaseEapFromBranch")) {
|
|
||||||
vcs {
|
|
||||||
|
|
||||||
check(branchFilter == "+:heads/(releases/*)") {
|
|
||||||
"Unexpected option value: branchFilter = $branchFilter"
|
|
||||||
}
|
|
||||||
branchFilter = "heads/releases/*"
|
|
||||||
}
|
|
||||||
}
|
|
44
AUTHORS.md
44
AUTHORS.md
@@ -523,6 +523,46 @@ Contributors:
|
|||||||
[![icon][github]](https://github.com/LazyScaper)
|
[![icon][github]](https://github.com/LazyScaper)
|
||||||
|
|
||||||
Jake
|
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
|
||||||
|
* [![icon][mail]](mailto:533601+felixwiemuth@users.noreply.github.com)
|
||||||
|
[![icon][github]](https://github.com/felixwiemuth)
|
||||||
|
|
||||||
|
Felix Wiemuth
|
||||||
|
* [![icon][mail]](mailto:kirill.karnaukhov@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/kkarnauk)
|
||||||
|
|
||||||
|
Kirill Karnaukhov,
|
||||||
|
* [![icon][mail]](mailto:sander.hestvik@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/SanderHestvik)
|
||||||
|
|
||||||
|
SanderHestvik
|
||||||
|
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/gregsh)
|
||||||
|
|
||||||
|
Greg Shrago
|
||||||
|
* [![icon][mail]](mailto:jphalip@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/jphalip)
|
||||||
|
|
||||||
|
Julien Phalip
|
||||||
|
* [![icon][mail]](mailto:j.trimailovas@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/trimailov)
|
||||||
|
|
||||||
|
Justas Trimailovas,
|
||||||
|
* [![icon][mail]](mailto:justast@wix.com)
|
||||||
|
[![icon][github]](https://github.com/justast-wix)
|
||||||
|
|
||||||
|
Justas Trimailovas
|
||||||
|
|
||||||
Previous contributors:
|
Previous contributors:
|
||||||
|
|
||||||
@@ -534,6 +574,10 @@ Previous contributors:
|
|||||||
[![icon][github]](https://github.com/kevin70)
|
[![icon][github]](https://github.com/kevin70)
|
||||||
|
|
||||||
kk
|
kk
|
||||||
|
* [![icon][mail]](mailto:gregory.shrago@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/gregsh)
|
||||||
|
|
||||||
|
Greg Shrago
|
||||||
|
|
||||||
|
|
||||||
If you are a contributor and your name is not listed here, feel free to
|
If you are a contributor and your name is not listed here, feel free to
|
||||||
|
@@ -27,8 +27,8 @@ usual beta standards.
|
|||||||
|
|
||||||
Since version 2.9.0, the changelog can be found on YouTrack
|
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
|
* [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
|
* [Version Fixes](https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20%7BFix%20versions%7D%20asc)
|
||||||
|
|
||||||
## 2.9.0, 2024-02-20
|
## 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:
|
If you are looking for:
|
||||||
|
|
||||||
- Vim commands (`w`, `<C-O>`, `p`, etc.):
|
- 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`.
|
- How commands are executed in common: `EditorActionHandlerBase`.
|
||||||
- Key mapping: `KeyHandler.handleKey()`.
|
- Key mapping: `KeyHandler.handleKey()`.
|
||||||
|
|
||||||
- Ex commands (`:set`, `:s`, `:nohlsearch`):
|
- Ex commands (`:set`, `:s`, `:nohlsearch`):
|
||||||
- Any particular 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 grammar: `Vimscript.g4`.
|
||||||
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
|
- Vim script parsing: package `com.maddyhome.idea.vim.vimscript.parser`.
|
||||||
- Vim script executor: `Executor`.
|
- Vim script executor: `Executor`.
|
||||||
|
@@ -109,7 +109,6 @@ etc
|
|||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
|
||||||
* [The list of all supported commands](src/main/java/com/maddyhome/idea/vim/package-info.java)
|
|
||||||
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
|
* [Top feature requests and bugs](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
|
||||||
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
|
* [Vimscript support roadmap](vimscript-info/VIMSCRIPT_ROADMAP.md)
|
||||||
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
|
* [List of supported in-build functions](vimscript-info/FUNCTIONS_INFO.MD)
|
||||||
@@ -222,13 +221,13 @@ Ex commands or via `:map` command mappings:
|
|||||||
* Execute an action by `{action_id}`. Works from Ex command line.
|
* Execute an action by `{action_id}`. Works from Ex command line.
|
||||||
* Please don't use `:action` in mappings. Use `<Action>` instead.
|
* Please don't use `:action` in mappings. Use `<Action>` instead.
|
||||||
|
|
||||||
### Finding action ids:
|
### Finding action IDs:
|
||||||
|
|
||||||
* IJ provides `IdeaVim: track action Ids` command to show the id of the executed actions.
|
* IJ provides `IdeaVim: track action IDs` command to show the id of the executed actions.
|
||||||
This command can be found in "Search everywhere" (double `shift`).
|
This command can be found in "Search everywhere" (double `shift`).
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
|
<summary><strong>"Track action IDs" Details</strong> (click to see)</summary>
|
||||||
<picture>
|
<picture>
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
|
<source media="(prefers-color-scheme: dark)" srcset="assets/readme/track_action_dark.gif">
|
||||||
<img src="assets/readme/track_action_light.gif" alt="track action ids"/>
|
<img src="assets/readme/track_action_light.gif" alt="track action ids"/>
|
||||||
@@ -369,6 +368,8 @@ is the full list of synonyms.
|
|||||||
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
|
- Fancy constants for [undolevels](https://vimhelp.org/options.txt.html#%27undolevels%27):
|
||||||
> The local value is set to -123456 when the global value is to be used.
|
> The local value is set to -123456 when the global value is to be used.
|
||||||
|
|
||||||
|
- Vi (not Vim) is a POSIX standard, and [has a spec](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/vi.html)! Vim is mostly POSIX compliant when Vi compatibility is selected with the `'compatible'` option, but there are still some differences that can be changed with `'copoptions'`. The spec is interesting because it documents the behaviour of different commands in a stricter style than the user documentation, describing the current line and column after the command, for example. [More details can be found by reading `:help posix`](https://vimhelp.org/vi_diff.txt.html#posix).
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
kotlin("plugin.serialization") version "1.9.22"
|
kotlin("plugin.serialization") version "2.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
val kotlinxSerializationVersion: String by project
|
val kotlinxSerializationVersion: String by project
|
||||||
@@ -21,7 +21,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.22")
|
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.1.0-1.0.29")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
||||||
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
@@ -37,7 +37,8 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
|
|||||||
Files.createDirectories(generatedDirPath)
|
Files.createDirectories(generatedDirPath)
|
||||||
|
|
||||||
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
|
val filePath = generatedDirPath.resolve(environment.options["commands_file"]!!)
|
||||||
val fileContent = json.encodeToString(commands)
|
val sortedCommands = commands.sortedWith(compareBy({ it.keys }, { it.`class` }))
|
||||||
|
val fileContent = json.encodeToString(sortedCommands)
|
||||||
filePath.writeText(fileContent)
|
filePath.writeText(fileContent)
|
||||||
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
@@ -37,7 +37,8 @@ class ExCommandProcessor(private val environment: SymbolProcessorEnvironment): S
|
|||||||
Files.createDirectories(generatedDirPath)
|
Files.createDirectories(generatedDirPath)
|
||||||
|
|
||||||
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
|
val filePath = generatedDirPath.resolve(environment.options["ex_commands_file"]!!)
|
||||||
val fileContent = json.encodeToString(commandToClass)
|
val sortedCommandToClass = commandToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
|
||||||
|
val fileContent = json.encodeToString(sortedCommandToClass)
|
||||||
filePath.writeText(fileContent)
|
filePath.writeText(fileContent)
|
||||||
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
@@ -37,7 +37,8 @@ class VimscriptFunctionProcessor(private val environment: SymbolProcessorEnviron
|
|||||||
Files.createDirectories(generatedDirPath)
|
Files.createDirectories(generatedDirPath)
|
||||||
|
|
||||||
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
|
val filePath = generatedDirPath.resolve(environment.options["vimscript_functions_file"]!!)
|
||||||
val fileContent = json.encodeToString(nameToClass)
|
val sortedNameToClass = nameToClass.toList().sortedWith(compareBy({ it.first }, { it.second })).toMap()
|
||||||
|
val fileContent = json.encodeToString(sortedNameToClass)
|
||||||
filePath.writeText(fileContent)
|
filePath.writeText(fileContent)
|
||||||
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
229
build.gradle.kts
229
build.gradle.kts
@@ -32,6 +32,8 @@ import org.eclipse.jgit.api.Git
|
|||||||
import org.eclipse.jgit.lib.RepositoryBuilder
|
import org.eclipse.jgit.lib.RepositoryBuilder
|
||||||
import org.intellij.markdown.ast.getTextInNode
|
import org.intellij.markdown.ast.getTextInNode
|
||||||
import org.jetbrains.changelog.Changelog
|
import org.jetbrains.changelog.Changelog
|
||||||
|
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
|
||||||
|
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
|
||||||
import org.kohsuke.github.GHUser
|
import org.kohsuke.github.GHUser
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@@ -43,19 +45,19 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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("com.github.AlexPl292:mark-down-to-slack:1.1.2")
|
||||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||||
|
|
||||||
// This is needed for jgit to connect to ssh
|
// This is needed for jgit to connect to ssh
|
||||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
|
||||||
classpath("org.kohsuke:github-api:1.305")
|
classpath("org.kohsuke:github-api:1.305")
|
||||||
|
|
||||||
classpath("io.ktor:ktor-client-core:2.3.12")
|
classpath("io.ktor:ktor-client-core:3.0.2")
|
||||||
classpath("io.ktor:ktor-client-cio:2.3.10")
|
classpath("io.ktor:ktor-client-cio:3.0.2")
|
||||||
classpath("io.ktor:ktor-client-auth:2.3.12")
|
classpath("io.ktor:ktor-client-auth:3.0.2")
|
||||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
classpath("io.ktor:ktor-client-content-negotiation:3.0.2")
|
||||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
classpath("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
|
||||||
|
|
||||||
// This comes from the changelog plugin
|
// This comes from the changelog plugin
|
||||||
// classpath("org.jetbrains:markdown:0.3.1")
|
// classpath("org.jetbrains:markdown:0.3.1")
|
||||||
@@ -64,28 +66,25 @@ buildscript {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
kotlin("jvm") version "1.9.22"
|
kotlin("jvm") version "2.0.0"
|
||||||
application
|
application
|
||||||
id("java-test-fixtures")
|
id("java-test-fixtures")
|
||||||
|
id("org.jetbrains.intellij.platform") version "2.2.0"
|
||||||
id("org.jetbrains.intellij") version "1.17.3"
|
id("org.jetbrains.changelog") version "2.2.1"
|
||||||
id("org.jetbrains.changelog") version "2.2.0"
|
|
||||||
|
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||||
id("com.dorongold.task-tree") version "4.0.0"
|
id("com.dorongold.task-tree") version "4.0.0"
|
||||||
|
id("com.google.devtools.ksp") version "2.0.0-1.0.23"
|
||||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val moduleSources by configurations.registering
|
||||||
|
|
||||||
// Import variables from gradle.properties file
|
// Import variables from gradle.properties file
|
||||||
val javaVersion: String by project
|
val javaVersion: String by project
|
||||||
val kotlinVersion: String by project
|
val kotlinVersion: String by project
|
||||||
val ideaVersion: String by project
|
val ideaVersion: String by project
|
||||||
val ideaType: String by project
|
val ideaType: String by project
|
||||||
val downloadIdeaSources: String by project
|
|
||||||
val instrumentPluginCode: String by project
|
val instrumentPluginCode: String by project
|
||||||
val remoteRobotVersion: String by project
|
val remoteRobotVersion: String by project
|
||||||
val splitModeVersion: String by project
|
|
||||||
|
|
||||||
val publishChannels: String by project
|
val publishChannels: String by project
|
||||||
val publishToken: String by project
|
val publishToken: String by project
|
||||||
@@ -97,7 +96,9 @@ val releaseType: String? by project
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
|
intellijPlatform {
|
||||||
|
defaultRepositories()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -106,12 +107,34 @@ dependencies {
|
|||||||
compileOnly(project(":annotation-processors"))
|
compileOnly(project(":annotation-processors"))
|
||||||
|
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
||||||
compileOnly("org.jetbrains:annotations:24.1.0")
|
compileOnly("org.jetbrains:annotations:26.0.1")
|
||||||
|
|
||||||
|
intellijPlatform {
|
||||||
|
// Snapshots don't use installers
|
||||||
|
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html#target-versions-installers
|
||||||
|
val useInstaller = "EAP-SNAPSHOT" !in ideaVersion
|
||||||
|
|
||||||
|
// Note that it is also possible to use local("...") to compile against a locally installed IDE
|
||||||
|
// E.g. local("/Users/{user}/Applications/IntelliJ IDEA Ultimate.app")
|
||||||
|
// Or something like: intellijIdeaUltimate(ideaVersion)
|
||||||
|
create(ideaType, ideaVersion, useInstaller)
|
||||||
|
|
||||||
|
pluginVerifier()
|
||||||
|
zipSigner()
|
||||||
|
instrumentationTools()
|
||||||
|
|
||||||
|
testFramework(TestFrameworkType.Platform)
|
||||||
|
testFramework(TestFrameworkType.JUnit5)
|
||||||
|
|
||||||
|
// AceJump is an optional dependency. We use their SessionManager class to check if it's active
|
||||||
|
plugin("AceJump", "3.8.19")
|
||||||
|
plugin("com.intellij.classic.ui", "242.20224.159")
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleSources(project(":vim-engine", "sourcesJarArtifacts"))
|
||||||
|
|
||||||
// --------- Test dependencies ----------
|
// --------- Test dependencies ----------
|
||||||
|
|
||||||
testImplementation(testFixtures(project(":")))
|
|
||||||
|
|
||||||
testApi("com.squareup.okhttp3:okhttp:4.12.0")
|
testApi("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
|
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
|
||||||
@@ -125,14 +148,20 @@ dependencies {
|
|||||||
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.5")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.5")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.5")
|
||||||
|
|
||||||
|
// Temp workaround suggested in https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
|
||||||
|
// Can be removed when IJPL-159134 is fixed
|
||||||
|
// testRuntimeOnly("junit:junit:4.13.2")
|
||||||
|
testImplementation("org.junit.vintage:junit-vintage-engine:5.10.5")
|
||||||
|
// testFixturesImplementation("org.junit.vintage:junit-vintage-engine:5.10.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
@@ -172,60 +201,91 @@ tasks {
|
|||||||
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
|
// See https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library
|
||||||
// For the list of bundled versions
|
// For the list of bundled versions
|
||||||
apiVersion = "1.9"
|
apiVersion = "1.9"
|
||||||
freeCompilerArgs = listOf("-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
|
// allWarningsAsErrors = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTestKotlin {
|
compileTestKotlin {
|
||||||
|
enabled = false
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = javaVersion
|
jvmTarget = javaVersion
|
||||||
apiVersion = "1.9"
|
apiVersion = "1.9"
|
||||||
|
|
||||||
|
// Needed to compile the AceJump which uses kotlin beta
|
||||||
|
// Without these two option compilation fails
|
||||||
|
freeCompilerArgs += listOf("-Xskip-prerelease-check", "-Xallow-unstable-dependencies")
|
||||||
// allWarningsAsErrors = true
|
// allWarningsAsErrors = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that this will run the plugin installed in the IDE specified in dependencies. To run in a different IDE, use
|
||||||
|
// a custom task (see below)
|
||||||
runIde {
|
runIde {
|
||||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadRobotServerPlugin {
|
// Uncomment to run the plugin in a custom IDE, rather than the IDE specified as a compile target in dependencies
|
||||||
version.set(remoteRobotVersion)
|
// Note that the version must be greater than the plugin's target version, for obvious reasons
|
||||||
|
// You can also set splitMode and splitModeTarget here to test split mode in a custom IDE
|
||||||
|
// 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),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runIdeForUiTests {
|
plugins {
|
||||||
systemProperty("robot-server.port", "8082")
|
robotServerPlugin(remoteRobotVersion)
|
||||||
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)
|
val runIdeSplitMode by intellijPlatformTesting.runIde.registering {
|
||||||
|
splitMode = true
|
||||||
|
splitModeTarget = SplitModeAware.SplitModeTarget.FRONTEND
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add plugin open API sources to the plugin ZIP
|
// Add plugin open API sources to the plugin ZIP
|
||||||
val createOpenApiSourceJar by registering(Jar::class) {
|
val sourcesJar by registering(Jar::class) {
|
||||||
// Java sources
|
dependsOn(moduleSources)
|
||||||
from(sourceSets.main.get().java) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
|
||||||
}
|
|
||||||
from(project(":vim-engine").sourceSets.main.get().java) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
|
||||||
}
|
|
||||||
// Kotlin sources
|
|
||||||
from(kotlin.sourceSets.main.get().kotlin) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
|
||||||
}
|
|
||||||
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
|
||||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
|
||||||
}
|
|
||||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||||
archiveClassifier.set("src")
|
archiveClassifier.set(DocsType.SOURCES)
|
||||||
|
from(sourceSets.main.map { it.kotlin })
|
||||||
|
from(provider {
|
||||||
|
moduleSources.map {
|
||||||
|
it.map { jarFile -> zipTree(jarFile) }
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPlugin {
|
buildPlugin {
|
||||||
dependsOn(createOpenApiSourceJar)
|
dependsOn(sourcesJar)
|
||||||
from(createOpenApiSourceJar) { into("lib/src") }
|
from(sourcesJar) { into("lib/src") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,44 +310,51 @@ gradle.projectsEvaluated {
|
|||||||
|
|
||||||
// --- Intellij plugin
|
// --- Intellij plugin
|
||||||
|
|
||||||
intellij {
|
intellijPlatform {
|
||||||
version.set(ideaVersion)
|
pluginConfiguration {
|
||||||
type.set(ideaType)
|
name = "IdeaVim"
|
||||||
pluginName.set("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
|
||||||
downloadSources.set(downloadIdeaSources.toBoolean())
|
// specified as two components, `{branch}.{build}` (e.g., "241.15989"). There is no third component specified.
|
||||||
instrumentCode.set(instrumentPluginCode.toBoolean())
|
// The until-build version defaults to `{branch}.*`, but we want to support _all_ future versions, so we set it
|
||||||
intellijRepository.set("https://www.jetbrains.com/intellij-repository")
|
// with a null provider (the provider is important).
|
||||||
plugins.set(listOf("AceJump:3.8.11"))
|
// 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 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
publishing {
|
||||||
publishPlugin {
|
|
||||||
channels.set(publishChannels.split(","))
|
channels.set(publishChannels.split(","))
|
||||||
token.set(publishToken)
|
token.set(publishToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
signPlugin {
|
signing {
|
||||||
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
|
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
|
||||||
privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
|
privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
|
||||||
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
|
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
|
||||||
}
|
}
|
||||||
|
|
||||||
runPluginVerifier {
|
verifyPlugin {
|
||||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
teamCityOutputFormat = true
|
||||||
teamCityOutputFormat.set(true)
|
ides {
|
||||||
|
recommended()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
patchPluginXml {
|
instrumentCode.set(instrumentPluginCode.toBoolean())
|
||||||
// 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>"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ksp {
|
ksp {
|
||||||
@@ -891,12 +958,12 @@ fun changes(): List<Change> {
|
|||||||
println("Start changes processing")
|
println("Start changes processing")
|
||||||
for (message in messages) {
|
for (message in messages) {
|
||||||
println("Processing '$message'...")
|
println("Processing '$message'...")
|
||||||
val lowercaseMessage = message.toLowerCase()
|
val lowercaseMessage = message.lowercase()
|
||||||
val regex = "^fix\\((vim-\\d+)\\):".toRegex()
|
val regex = "^fix\\((vim-\\d+)\\):".toRegex()
|
||||||
val findResult = regex.find(lowercaseMessage)
|
val findResult = regex.find(lowercaseMessage)
|
||||||
if (findResult != null) {
|
if (findResult != null) {
|
||||||
println("Message matches")
|
println("Message matches")
|
||||||
val value = findResult.groups[1]!!.value.toUpperCase()
|
val value = findResult.groups[1]!!.value.uppercase()
|
||||||
val shortMessage = message.drop(findResult.range.last + 1).trim()
|
val shortMessage = message.drop(findResult.range.last + 1).trim()
|
||||||
newFixes += Change(value, shortMessage)
|
newFixes += Change(value, shortMessage)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -322,6 +322,9 @@ If you want to optimize highlight duration, assign a time in milliseconds:
|
|||||||
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
|
If you want to change background color of highlight you can provide the rgba of the color you want e.g.
|
||||||
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
|
`let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"`
|
||||||
|
|
||||||
|
If you want to change text color of highlight you can provide the rgba of the color you want e.g.
|
||||||
|
`let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"`
|
||||||
|
|
||||||
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
|
https://github.com/machakann/vim-highlightedyank/blob/master/doc/highlightedyank.txt
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
@@ -435,3 +438,50 @@ Original plugin: [vim-which-key](https://github.com/liuchengxu/vim-which-key).
|
|||||||
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
|
https://github.com/TheBlob42/idea-which-key?tab=readme-ov-file#installation
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary><h2>Vim Peekaboo</h2></summary>
|
||||||
|
|
||||||
|
By Julien Phalip
|
||||||
|
Original plugin: [vim-peekaboo](https://github.com/junegunn/vim-peekaboo).
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Add `set peekaboo` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
|
||||||
|
or restart the IDE.
|
||||||
|
|
||||||
|
### Instructions
|
||||||
|
|
||||||
|
https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h2>FunctionTextObj</h2></summary>
|
||||||
|
|
||||||
|
By Julien Phalip
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Add `set functiontextobj` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
|
||||||
|
or restart the IDE.
|
||||||
|
|
||||||
|
### Instructions
|
||||||
|
|
||||||
|
https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><h2>Switch</h2></summary>
|
||||||
|
|
||||||
|
By Julien Phalip
|
||||||
|
Original plugin: [switch.vim](https://github.com/AndrewRadev/switch.vim).
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Add `set switch` to your `~/.ideavimrc` file, then run `:source ~/.ideavimrc`
|
||||||
|
or restart the IDE.
|
||||||
|
|
||||||
|
### Instructions
|
||||||
|
|
||||||
|
https://plugins.jetbrains.com/plugin/25899-vim-switch
|
||||||
|
|
||||||
|
</details>
|
||||||
|
@@ -5,7 +5,7 @@ Using actions from external plugins is the same, as tracking and reusing any IDE
|
|||||||
1. Install the plugin via Marketplace
|
1. Install the plugin via Marketplace
|
||||||
2. Enable action tracking. You can enable it by one of the following ways:
|
2. Enable action tracking. You can enable it by one of the following ways:
|
||||||
* Execute `:set trackactionids` ex command or just `:set tai`
|
* Execute `:set trackactionids` ex command or just `:set tai`
|
||||||
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action Ids" to find the toggle that enables and disables action tracking
|
* Open the "Find actions" window by pressing `Ctrl-Shift-A` and search for "Track Action IDs" to find the toggle that enables and disables action tracking
|
||||||
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
|
3. Execute the plugin action the way intended by plugin author "See Edit menu or use ⇧ + ⌥ + U / Shift + Alt + U" or just find the `Toggle Camel Case` action in the "Find actions" window (`Ctrl-Shift-A`). If you action tracking is on, you will see this notification in your right bottom corner:
|
||||||
|
|
||||||
<img alt="Notification" src="images/action-id-notification.png"/>
|
<img alt="Notification" src="images/action-id-notification.png"/>
|
||||||
|
@@ -8,7 +8,7 @@ Every effort is made to make these options compatible with Vim behaviour.
|
|||||||
However, some differences are inevitable.
|
However, some differences are inevitable.
|
||||||
|
|
||||||
```
|
```
|
||||||
'clipboard' 'cb' Defines clipboard behavioue
|
'clipboard' 'cb' Defines clipboard behavior
|
||||||
A comma-separated list of words to control clipboard behaviour:
|
A comma-separated list of words to control clipboard behaviour:
|
||||||
unnamed The clipboard register '*' is used instead of the
|
unnamed The clipboard register '*' is used instead of the
|
||||||
unnamed register
|
unnamed register
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
# Support Guide
|
# Support Guide
|
||||||
|
|
||||||
This document is created to help our support team.
|
This document is created to help our support team.
|
||||||
It's not intended to be read by the users as it brings to value to them.
|
It's not intended to be read by the users as it brings no value to them.
|
||||||
|
|
||||||
## Support channels
|
## Support channels
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ It's not intended to be read by the users as it brings to value to them.
|
|||||||
IdeaVim has multiple YouTrack statuses, main are:
|
IdeaVim has multiple YouTrack statuses, main are:
|
||||||
|
|
||||||
- Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets.
|
- Submitted: issue is created by user, but not processed by our team. This is the default status for new tickets.
|
||||||
- Open: issues is processed by out team, what means that the issues is reproduced and accepted
|
- Open: issues is processed by our team, what means that the issues is reproduced and accepted
|
||||||
- Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the
|
- Waiting For Reply: Waiting for further information from the user. These issues are automatically closed if the
|
||||||
user doesn't reply in 30 days.
|
user doesn't reply in 30 days.
|
||||||
- Ready To Release: Bug is fixed, but not yet released
|
- Ready To Release: Bug is fixed, but not yet released
|
||||||
|
@@ -16,26 +16,19 @@
|
|||||||
# https://data.services.jetbrains.com/products?code=IC
|
# https://data.services.jetbrains.com/products?code=IC
|
||||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
||||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
||||||
ideaVersion=2024.1.1
|
ideaVersion=2024.2
|
||||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||||
ideaType=IC
|
ideaType=IC
|
||||||
downloadIdeaSources=true
|
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=chylex-36
|
version=chylex-42
|
||||||
javaVersion=17
|
javaVersion=17
|
||||||
remoteRobotVersion=0.11.22
|
remoteRobotVersion=0.11.23
|
||||||
antlrVersion=4.10.1
|
antlrVersion=4.10.1
|
||||||
|
|
||||||
# [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
|
# Please don't forget to update kotlin version in buildscript section
|
||||||
# Also update kotlinxSerializationVersion version
|
# Also update kotlinxSerializationVersion version
|
||||||
kotlinVersion=1.9.22
|
kotlinVersion=2.0.0
|
||||||
publishToken=token
|
publishToken=token
|
||||||
publishChannels=eap
|
publishChannels=eap
|
||||||
|
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
22
gradlew
vendored
22
gradlew
vendored
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -83,7 +85,9 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -201,11 +205,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
@@ -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/JavaText.kt
|
||||||
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
|
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/LoremText.kt
|
||||||
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
|
- src/test/java/org/jetbrains/plugins/ideavim/propertybased/samples/SimpleText.kt
|
||||||
- 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/regexp/parser/generated
|
||||||
|
- vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
|
||||||
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
|
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
|
||||||
- tests/ui-fixtures
|
- tests/ui-fixtures
|
||||||
dependencyIgnores:
|
dependencyIgnores:
|
||||||
|
@@ -20,17 +20,17 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.0")
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core:2.3.12")
|
implementation("io.ktor:ktor-client-core:3.0.2")
|
||||||
implementation("io.ktor:ktor-client-cio:2.3.10")
|
implementation("io.ktor:ktor-client-cio:3.0.2")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
implementation("io.ktor:ktor-client-content-negotiation:3.0.2")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.2")
|
||||||
implementation("io.ktor:ktor-client-auth:2.3.12")
|
implementation("io.ktor:ktor-client-auth:3.0.2")
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||||
|
|
||||||
// This is needed for jgit to connect to ssh
|
// This is needed for jgit to connect to ssh
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:7.1.0.202411261347-r")
|
||||||
implementation("com.vdurmont:semver4j:3.1.0")
|
implementation("com.vdurmont:semver4j:3.1.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -40,6 +40,9 @@ val knownPlugins = setOf(
|
|||||||
"com.protoseo.input-source-auto-converter",
|
"com.protoseo.input-source-auto-converter",
|
||||||
|
|
||||||
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
|
// "cc.implicated.intellij.plugins.bunny", // I don't want to include this plugin in the list of IdeaVim plugins as I don't understand what this is for
|
||||||
|
"com.julienphalip.ideavim.peekaboo", // https://plugins.jetbrains.com/plugin/25776-vim-peekaboo
|
||||||
|
"com.julienphalip.ideavim.switch", // https://plugins.jetbrains.com/plugin/25899-vim-switch
|
||||||
|
"com.julienphalip.ideavim.functiontextobj", // https://plugins.jetbrains.com/plugin/25897-vim-functiontextobj
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun main() {
|
suspend fun main() {
|
||||||
|
@@ -8,14 +8,23 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim
|
package com.maddyhome.idea.vim
|
||||||
|
|
||||||
|
import com.intellij.ide.BrowserUtil
|
||||||
|
import com.intellij.ide.plugins.IdeaPluginDescriptor
|
||||||
|
import com.intellij.ide.plugins.PluginStateListener
|
||||||
|
import com.intellij.ide.plugins.PluginStateManager
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.project.ProjectManagerListener
|
import com.intellij.openapi.project.ProjectManagerListener
|
||||||
import com.intellij.openapi.startup.ProjectActivity
|
import com.intellij.openapi.startup.ProjectActivity
|
||||||
|
import com.intellij.openapi.updateSettings.impl.UpdateSettings
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
|
import com.maddyhome.idea.vim.ui.JoinEap
|
||||||
|
import com.maddyhome.idea.vim.ui.JoinEap.EAP_LINK
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
@@ -24,18 +33,39 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
|||||||
|
|
||||||
private var firstInitializationOccurred = false
|
private var firstInitializationOccurred = false
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// We should migrate to some solution from https://plugins.jetbrains.com/docs/intellij/plugin-components.html#application-startup
|
||||||
|
// If you'd like to add a new code here, please consider using one of the things described there.
|
||||||
override suspend fun execute(project: Project) {
|
override suspend fun execute(project: Project) {
|
||||||
if (firstInitializationOccurred) return
|
if (firstInitializationOccurred) return
|
||||||
firstInitializationOccurred = true
|
firstInitializationOccurred = true
|
||||||
|
|
||||||
|
if (!VimPlugin.getVimState().wasSubscibedToEAPAutomatically && ApplicationManager.getApplication().isEAP && !JoinEap.eapActive()) {
|
||||||
|
VimPlugin.getVimState().wasSubscibedToEAPAutomatically = true
|
||||||
|
UpdateSettings.getInstance().storedPluginHosts += EAP_LINK
|
||||||
|
}
|
||||||
|
|
||||||
// This code should be executed once
|
// This code should be executed once
|
||||||
VimPlugin.getInstance().initialize()
|
VimPlugin.getInstance().initialize()
|
||||||
|
|
||||||
|
// Uninstall survey. Should be registered once for all projects
|
||||||
|
PluginStateManager.addStateListener(object : PluginStateListener {
|
||||||
|
override fun install(p0: IdeaPluginDescriptor) {/*Nothing*/
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uninstall(descriptor: IdeaPluginDescriptor) {
|
||||||
|
if (descriptor.pluginId == VimPlugin.getPluginId()) {
|
||||||
|
BrowserUtil.open("https://surveys.jetbrains.com/s3/ideavim-uninstall-feedback")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a temporal workaround for VIM-2487
|
// This is a temporal workaround for VIM-2487
|
||||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
|
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
|
||||||
override fun projectClosingBeforeSave(project: Project) {
|
override fun projectClosingBeforeSave(project: Project) {
|
||||||
|
initInjector()
|
||||||
// TODO: Confirm context in CWM scenario
|
// TODO: Confirm context in CWM scenario
|
||||||
if (injector.globalIjOptions().closenotebooks) {
|
if (injector.globalIjOptions().closenotebooks) {
|
||||||
injector.editorGroup.getEditors().forEach { vimEditor ->
|
injector.editorGroup.getEditors().forEach { vimEditor ->
|
||||||
|
@@ -10,6 +10,7 @@ package com.maddyhome.idea.vim;
|
|||||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||||
import com.intellij.ide.plugins.PluginManagerCore;
|
import com.intellij.ide.plugins.PluginManagerCore;
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
|
import com.intellij.openapi.application.AccessToken;
|
||||||
import com.intellij.openapi.application.Application;
|
import com.intellij.openapi.application.Application;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.components.PersistentStateComponent;
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
@@ -24,10 +25,8 @@ import com.intellij.openapi.project.Project;
|
|||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
import com.intellij.openapi.util.Disposer;
|
import com.intellij.openapi.util.Disposer;
|
||||||
import com.intellij.openapi.util.SystemInfo;
|
import com.intellij.openapi.util.SystemInfo;
|
||||||
import com.maddyhome.idea.vim.api.VimEditor;
|
import com.intellij.util.SlowOperations;
|
||||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.api.VimKeyGroup;
|
|
||||||
import com.maddyhome.idea.vim.api.VimOptionGroup;
|
|
||||||
import com.maddyhome.idea.vim.config.VimState;
|
import com.maddyhome.idea.vim.config.VimState;
|
||||||
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
|
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
|
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
|
||||||
@@ -36,7 +35,7 @@ import com.maddyhome.idea.vim.group.copy.PutGroup;
|
|||||||
import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
|
import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
|
||||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
||||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
||||||
@@ -67,7 +66,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
VimInjectorKt.setInjector(new IjVimInjector());
|
IjVimInjectorKt.initInjector();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final @NotNull VimState state = new VimState();
|
private final @NotNull VimState state = new VimState();
|
||||||
@@ -140,8 +139,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
return (MacroGroup)VimInjectorKt.getInjector().getMacro();
|
return (MacroGroup)VimInjectorKt.getInjector().getMacro();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull DigraphGroup getDigraph() {
|
public static @NotNull VimDigraphGroup getDigraph() {
|
||||||
return (DigraphGroup)VimInjectorKt.getInjector().getDigraphGroup();
|
return VimInjectorKt.getInjector().getDigraphGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull HistoryGroup getHistory() {
|
public static @NotNull HistoryGroup getHistory() {
|
||||||
@@ -338,7 +337,9 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
|
|
||||||
// 4) ~/.ideavimrc execution
|
// 4) ~/.ideavimrc execution
|
||||||
// Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window
|
// Evaluate in the context of the fallback window, to capture local option state, to copy to the first editor window
|
||||||
|
try (AccessToken ignore = SlowOperations.knownIssue("VIM-3661")) {
|
||||||
registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
|
registerIdeavimrc(VimInjectorKt.getInjector().getFallbackWindow());
|
||||||
|
}
|
||||||
|
|
||||||
// Turing on should be performed after all commands registration
|
// Turing on should be performed after all commands registration
|
||||||
getSearch().turnOn();
|
getSearch().turnOn();
|
||||||
|
@@ -18,7 +18,7 @@ import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
|||||||
internal class VimProjectService(val project: Project) : Disposable {
|
internal class VimProjectService(val project: Project) : Disposable {
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
// Not sure if this is a best solution
|
// Not sure if this is a best solution
|
||||||
ExEntryPanel.getInstance().editor = null
|
ExEntryPanel.getInstance().setEditor(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -43,6 +43,7 @@ import com.maddyhome.idea.vim.key.ShortcutOwnerInfo
|
|||||||
import com.maddyhome.idea.vim.listener.AceJumpService
|
import com.maddyhome.idea.vim.listener.AceJumpService
|
||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
|
import com.maddyhome.idea.vim.ui.ex.ExTextField
|
||||||
@@ -61,10 +62,13 @@ import javax.swing.KeyStroke
|
|||||||
* way to get ideavim keys for this plugin. See VIM-3085
|
* way to get ideavim keys for this plugin. See VIM-3085
|
||||||
*/
|
*/
|
||||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||||
|
|
||||||
|
init {
|
||||||
|
initInjector()
|
||||||
|
}
|
||||||
|
|
||||||
private val traceTime: Boolean
|
private val traceTime: Boolean
|
||||||
get() {
|
get() {
|
||||||
// Make sure the injector is initialized
|
|
||||||
VimPlugin.getInstance()
|
|
||||||
return injector.globalOptions().ideatracetime
|
return injector.globalOptions().ideatracetime
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +261,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
private fun getEditor(e: AnActionEvent): Editor? {
|
private fun getEditor(e: AnActionEvent): Editor? {
|
||||||
return e.getData(PlatformDataKeys.EDITOR)
|
return e.getData(PlatformDataKeys.EDITOR)
|
||||||
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
|
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
|
||||||
ExEntryPanel.getInstance().editor
|
ExEntryPanel.getInstance().ijEditor
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -294,26 +298,10 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
|||||||
.addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0))
|
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0))
|
||||||
.addAll(
|
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0))
|
||||||
getKeyStrokes(
|
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0))
|
||||||
KeyEvent.VK_LEFT,
|
|
||||||
0,
|
|
||||||
InputEvent.CTRL_DOWN_MASK,
|
|
||||||
InputEvent.SHIFT_DOWN_MASK,
|
|
||||||
InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.addAll(
|
|
||||||
getKeyStrokes(
|
|
||||||
KeyEvent.VK_RIGHT,
|
|
||||||
0,
|
|
||||||
InputEvent.CTRL_DOWN_MASK,
|
|
||||||
InputEvent.SHIFT_DOWN_MASK,
|
|
||||||
InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.addAll(
|
.addAll(
|
||||||
getKeyStrokes(
|
getKeyStrokes(
|
||||||
KeyEvent.VK_HOME,
|
KeyEvent.VK_HOME,
|
||||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.group.visual.VimSelection
|
|||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
import com.maddyhome.idea.vim.helper.inRepeatMode
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||||
@@ -37,7 +37,7 @@ import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
|||||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
||||||
|
|
||||||
// todo make it multicaret
|
// todo make it multicaret
|
||||||
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
|
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, motionType: SelectionType): Boolean {
|
||||||
val func = injector.globalOptions().operatorfunc
|
val func = injector.globalOptions().operatorfunc
|
||||||
if (func.isEmpty()) {
|
if (func.isEmpty()) {
|
||||||
VimPlugin.showMessage(MessageHelper.message("E774"))
|
VimPlugin.showMessage(MessageHelper.message("E774"))
|
||||||
@@ -57,9 +57,9 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
|
|||||||
if (value is VimFuncref) {
|
if (value is VimFuncref) {
|
||||||
handler = value.handler
|
handler = value.handler
|
||||||
}
|
}
|
||||||
} catch (ex: ExException) {
|
} catch (_: ExException) {
|
||||||
// Get the argument for function('...') or funcref('...') for the error message
|
// Get the argument for function('...') or funcref('...') for the error message
|
||||||
val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
|
val functionName = if (expression is FunctionCallExpression && expression.arguments.isNotEmpty()) {
|
||||||
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -77,7 +77,7 @@ private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textR
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val arg = when (selectionType) {
|
val arg = when (motionType) {
|
||||||
SelectionType.LINE_WISE -> "line"
|
SelectionType.LINE_WISE -> "line"
|
||||||
SelectionType.CHARACTER_WISE -> "char"
|
SelectionType.CHARACTER_WISE -> "char"
|
||||||
SelectionType.BLOCK_WISE -> "block"
|
SelectionType.BLOCK_WISE -> "block"
|
||||||
@@ -101,19 +101,13 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
override val argumentType: Argument.Type = Argument.Type.MOTION
|
override val argumentType: Argument.Type = Argument.Type.MOTION
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
val argument = cmd.argument ?: return false
|
val argument = cmd.argument as? Argument.Motion ?: return false
|
||||||
if (!editor.vimStateMachine.isDotRepeatInProgress) {
|
if (!editor.inRepeatMode) {
|
||||||
argumentCaptured = argument
|
argumentCaptured = argument
|
||||||
}
|
}
|
||||||
val range = getMotionRange(editor, context, argument, operatorArguments)
|
val range = getMotionRange(editor, context, argument, operatorArguments)
|
||||||
|
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
val selectionType = if (argument.motion.isLinewiseMotion()) {
|
return doOperatorAction(editor, context, range, argument.getMotionType())
|
||||||
SelectionType.LINE_WISE
|
|
||||||
} else {
|
|
||||||
SelectionType.CHARACTER_WISE
|
|
||||||
}
|
|
||||||
return doOperatorAction(editor, context, range, selectionType)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -121,7 +115,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
private fun getMotionRange(
|
private fun getMotionRange(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
argument: Argument,
|
argument: Argument.Motion,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
// Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
|
// Note that we're using getMotionRange2 in order to avoid normalising the linewise range into line start
|
||||||
@@ -136,7 +130,7 @@ internal class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
operatorArguments,
|
operatorArguments,
|
||||||
)?.normalize()?.let {
|
)?.normalize()?.let {
|
||||||
// If we're linewise, make sure the end offset isn't just the EOL char
|
// If we're linewise, make sure the end offset isn't just the EOL char
|
||||||
if (argument.motion.isLinewiseMotion() && it.endOffset < editor.fileSize()) {
|
if (argument.getMotionType() == SelectionType.LINE_WISE && it.endOffset < editor.fileSize()) {
|
||||||
TextRange(it.startOffset, it.endOffset + 1)
|
TextRange(it.startOffset, it.endOffset + 1)
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
|
@@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.injector
|
|||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
|
@CommandOrMotion(keys = ["."], modes = [Mode.NORMAL])
|
||||||
@@ -25,8 +24,8 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
|||||||
override val type: Command.Type = Command.Type.OTHER_WRITABLE
|
override val type: Command.Type = Command.Type.OTHER_WRITABLE
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
val state = editor.vimStateMachine
|
val state = injector.vimState
|
||||||
val lastCommand = VimRepeater.lastChangeCommand
|
var lastCommand = VimRepeater.lastChangeCommand
|
||||||
|
|
||||||
if (lastCommand == null && Extension.lastExtensionHandler == null) return false
|
if (lastCommand == null && Extension.lastExtensionHandler == null) return false
|
||||||
|
|
||||||
@@ -58,12 +57,7 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
|||||||
)
|
)
|
||||||
} else if (!repeatHandler && lastCommand != null) {
|
} else if (!repeatHandler && lastCommand != null) {
|
||||||
if (cmd.rawCount > 0) {
|
if (cmd.rawCount > 0) {
|
||||||
lastCommand.rawCount = cmd.count
|
lastCommand = lastCommand.copy(rawCount = cmd.rawCount)
|
||||||
val arg = lastCommand.argument
|
|
||||||
if (arg != null) {
|
|
||||||
val mot = arg.motion
|
|
||||||
mot.rawCount = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
state.executingCommand = lastCommand
|
state.executingCommand = lastCommand
|
||||||
|
|
||||||
|
@@ -40,7 +40,7 @@ class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecuti
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
injector.editorGroup.notifyIdeaJoin(editor)
|
injector.editorGroup.notifyIdeaJoin(editor)
|
||||||
|
|
||||||
return injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, false, operatorArguments)
|
return injector.changeGroup.deleteJoinLines(editor, context, caret, operatorArguments.count1, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun execute(
|
override fun execute(
|
||||||
|
@@ -35,7 +35,7 @@ class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution()
|
|||||||
injector.editorGroup.notifyIdeaJoin(editor)
|
injector.editorGroup.notifyIdeaJoin(editor)
|
||||||
var res = true
|
var res = true
|
||||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||||
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
if (!injector.changeGroup.deleteJoinLines(editor, context, caret, operatorArguments.count1, true)) {
|
||||||
res = false
|
res = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,7 @@ class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution(
|
|||||||
val range = caretsAndSelections[caret] ?: return@forEach
|
val range = caretsAndSelections[caret] ?: return@forEach
|
||||||
if (!injector.changeGroup.deleteJoinRange(
|
if (!injector.changeGroup.deleteJoinRange(
|
||||||
editor,
|
editor,
|
||||||
|
context,
|
||||||
caret,
|
caret,
|
||||||
range.toVimTextRange(true).normalize(),
|
range.toVimTextRange(true).normalize(),
|
||||||
false,
|
false,
|
||||||
|
@@ -44,6 +44,7 @@ class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExec
|
|||||||
val range = caretsAndSelections[caret] ?: return@forEach
|
val range = caretsAndSelections[caret] ?: return@forEach
|
||||||
if (!injector.changeGroup.deleteJoinRange(
|
if (!injector.changeGroup.deleteJoinRange(
|
||||||
editor,
|
editor,
|
||||||
|
context,
|
||||||
caret,
|
caret,
|
||||||
range.toVimTextRange(true).normalize(),
|
range.toVimTextRange(true).normalize(),
|
||||||
true,
|
true,
|
||||||
|
@@ -20,13 +20,10 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
|||||||
import com.maddyhome.idea.vim.handler.IdeActionHandler
|
import com.maddyhome.idea.vim.handler.IdeActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
|
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
|
||||||
|
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
|
|
||||||
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
|
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
|
||||||
}
|
|
||||||
|
|
||||||
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
|
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
|
||||||
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
|
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
|
||||||
override val type: Command.Type = Command.Type.DELETE
|
override val type: Command.Type = Command.Type.DELETE
|
||||||
@@ -44,8 +41,13 @@ internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CA
|
|||||||
operatorArguments: OperatorArguments
|
operatorArguments: OperatorArguments
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val undo = injector.undo
|
val undo = injector.undo
|
||||||
|
when (undo) {
|
||||||
|
is VimKeyBasedUndoService -> undo.setMergeUndoKey()
|
||||||
|
is VimTimestampBasedUndoService -> {
|
||||||
val nanoTime = System.nanoTime()
|
val nanoTime = System.nanoTime()
|
||||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||||
|
}
|
||||||
|
}
|
||||||
return super.execute(editor, context, cmd, operatorArguments)
|
return super.execute(editor, context, cmd, operatorArguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,8 +70,13 @@ internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARE
|
|||||||
operatorArguments: OperatorArguments
|
operatorArguments: OperatorArguments
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val undo = injector.undo
|
val undo = injector.undo
|
||||||
|
when (undo) {
|
||||||
|
is VimKeyBasedUndoService -> undo.setMergeUndoKey()
|
||||||
|
is VimTimestampBasedUndoService -> {
|
||||||
val nanoTime = System.nanoTime()
|
val nanoTime = System.nanoTime()
|
||||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||||
|
}
|
||||||
|
}
|
||||||
return super.execute(editor, context, cmd, operatorArguments)
|
return super.execute(editor, context, cmd, operatorArguments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +86,7 @@ internal class VimQuickJavaDoc : VimActionHandler.SingleExecution() {
|
|||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
injector.actionExecutor.executeAction(IdeActions.ACTION_QUICK_JAVADOC, context)
|
injector.actionExecutor.executeAction(editor, IdeActions.ACTION_QUICK_JAVADOC, context)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
Normal file
72
src/main/java/com/maddyhome/idea/vim/command/CommandState.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2003-2023 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style
|
||||||
|
* license that can be found in the LICENSE.txt file or at
|
||||||
|
* https://opensource.org/licenses/MIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.command
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPATIBILITY-LAYER: Additional class
|
||||||
|
* Please see: https://jb.gg/zo8n0r
|
||||||
|
*/
|
||||||
|
@Deprecated("Use `injector.vimState`")
|
||||||
|
class CommandState(private val machine: VimStateMachine) {
|
||||||
|
|
||||||
|
val mode: Mode
|
||||||
|
get() {
|
||||||
|
val myMode = machine.mode
|
||||||
|
return when (myMode) {
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
|
||||||
|
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
|
||||||
|
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
|
||||||
|
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use `KeyHandler.keyHandlerState.commandBuilder", ReplaceWith(
|
||||||
|
"KeyHandler.getInstance().keyHandlerState.commandBuilder",
|
||||||
|
"com.maddyhome.idea.vim.KeyHandler"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val commandBuilder: CommandBuilder
|
||||||
|
get() = KeyHandler.getInstance().keyHandlerState.commandBuilder
|
||||||
|
|
||||||
|
@Deprecated("Use `KeyHandler.keyHandlerState.mappingState", ReplaceWith(
|
||||||
|
"KeyHandler.getInstance().keyHandlerState.mappingState",
|
||||||
|
"com.maddyhome.idea.vim.KeyHandler"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val mappingState: MappingState
|
||||||
|
get() = KeyHandler.getInstance().keyHandlerState.mappingState
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
// Basic modes
|
||||||
|
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
|
||||||
|
|
||||||
|
// Additional modes
|
||||||
|
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SubMode {
|
||||||
|
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
@Deprecated("Use `injector.vimState`")
|
||||||
|
fun getInstance(editor: Editor): CommandState {
|
||||||
|
return CommandState(injector.vimState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -12,17 +12,17 @@ import com.intellij.application.options.CodeStyle
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
||||||
|
import com.maddyhome.idea.vim.api.VimIndentConfig
|
||||||
|
|
||||||
internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
internal class IndentConfig private constructor(indentOptions: IndentOptions): VimIndentConfig {
|
||||||
private val indentSize = indentOptions.INDENT_SIZE
|
private val indentSize = indentOptions.INDENT_SIZE
|
||||||
private val tabSize = indentOptions.TAB_SIZE
|
private val tabSize = indentOptions.TAB_SIZE
|
||||||
private val isUseTabs = indentOptions.USE_TAB_CHARACTER
|
private val isUseTabs = indentOptions.USE_TAB_CHARACTER
|
||||||
|
|
||||||
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))
|
override fun createIndentBySize(size: Int): String {
|
||||||
|
|
||||||
fun createIndentBySize(size: Int): String {
|
|
||||||
val tabCount: Int
|
val tabCount: Int
|
||||||
val spaceCount: Int
|
val spaceCount: Int
|
||||||
if (isUseTabs) {
|
if (isUseTabs) {
|
||||||
|
@@ -18,6 +18,8 @@ import kotlin.reflect.KProperty
|
|||||||
internal class VimState {
|
internal class VimState {
|
||||||
var isIdeaJoinNotified by StateProperty("idea-join")
|
var isIdeaJoinNotified by StateProperty("idea-join")
|
||||||
var isIdeaPutNotified by StateProperty("idea-put")
|
var isIdeaPutNotified by StateProperty("idea-put")
|
||||||
|
var isNewUndoNotified by StateProperty("idea-undo")
|
||||||
|
var wasSubscibedToEAPAutomatically by StateProperty("was-automatically-subscribed-to-eap")
|
||||||
|
|
||||||
fun readData(element: Element) {
|
fun readData(element: Element) {
|
||||||
val notifications = element.getChild("notifications")
|
val notifications = element.getChild("notifications")
|
||||||
|
@@ -9,51 +9,124 @@ package com.maddyhome.idea.vim.ex
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.api.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.helper.vimExOutput
|
||||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
|
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
|
||||||
class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
|
class ExOutputModel(private val myEditor: WeakReference<Editor>) : VimOutputPanelBase() {
|
||||||
private var isActiveInTestMode = false
|
private var isActiveInTestMode = false
|
||||||
|
|
||||||
override val isActive: Boolean
|
val editor get() = myEditor.get()
|
||||||
|
|
||||||
|
val isActive: Boolean
|
||||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
ExOutputPanel.isPanelActive(myEditor)
|
editor?.let { ExOutputPanel.getNullablePanel(it) }?.myActive ?: false
|
||||||
} else {
|
} else {
|
||||||
isActiveInTestMode
|
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) {
|
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
ExOutputPanel.getInstance(myEditor).text
|
editor?.let { ExOutputPanel.getInstance(it).text } ?: ""
|
||||||
} else {
|
} else {
|
||||||
// ExOutputPanel always returns a non-null string
|
// ExOutputPanel always returns a non-null string
|
||||||
field ?: ""
|
field
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
|
// 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
|
// 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")
|
val newValue = value.removeSuffix("\n")
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
ExOutputPanel.getInstance(myEditor).setText(newValue ?: "")
|
editor?.let { ExOutputPanel.getInstance(it).setText(newValue) }
|
||||||
} else {
|
} else {
|
||||||
field = newValue
|
field = newValue
|
||||||
isActiveInTestMode = !newValue.isNullOrEmpty()
|
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
|
this.text = text
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clear() {
|
fun clear() {
|
||||||
text = null
|
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() {
|
override fun close() {
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
ExOutputPanel.getInstance(myEditor).close()
|
editor?.let { ExOutputPanel.getInstance(it).close() }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
isActiveInTestMode = false
|
isActiveInTestMode = false
|
||||||
@@ -65,7 +138,7 @@ class ExOutputModel private constructor(private val myEditor: Editor) : VimExOut
|
|||||||
fun getInstance(editor: Editor): ExOutputModel {
|
fun getInstance(editor: Editor): ExOutputModel {
|
||||||
var model = editor.vimExOutput
|
var model = editor.vimExOutput
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
model = ExOutputModel(editor)
|
model = ExOutputModel(WeakReference(editor))
|
||||||
editor.vimExOutput = model
|
editor.vimExOutput = model
|
||||||
}
|
}
|
||||||
return model
|
return model
|
||||||
|
@@ -27,6 +27,30 @@ public interface VimExtension {
|
|||||||
return MappingOwner.Plugin.Companion.get(getName());
|
return MappingOwner.Plugin.Companion.get(getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is always called AFTER the full execution of the `.ideavimrc` file.
|
||||||
|
* <p>
|
||||||
|
* During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
|
||||||
|
* This practically means that the .vimrc file is initialized first; then the plugins are loaded.
|
||||||
|
* See `:h initialization`
|
||||||
|
* <p>
|
||||||
|
* Why does this matter? Because this affects the order of commands are executed. For example,
|
||||||
|
* ```
|
||||||
|
* plug 'tommcdo/vim-exchange'
|
||||||
|
* let g:exchange_no_mappings=1
|
||||||
|
* ```
|
||||||
|
* Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
|
||||||
|
* immediately, this variable won't be initialized at the moment of plugin initialization.
|
||||||
|
* <p>
|
||||||
|
* There is also a tricky case for mappings override:
|
||||||
|
* ```
|
||||||
|
* plug 'tommcdo/vim-exchange'
|
||||||
|
* map X <Plug>(ExchangeLine)
|
||||||
|
* ```
|
||||||
|
* For this case, a plugin with a good implementation detects that there is already a defined mapping for
|
||||||
|
* `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
|
||||||
|
* to be defined before the plugin initialization.
|
||||||
|
*/
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
default void dispose() {
|
default void dispose() {
|
||||||
|
@@ -24,13 +24,14 @@ import com.maddyhome.idea.vim.common.CommandAlias
|
|||||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||||
|
import com.maddyhome.idea.vim.helper.inRepeatMode
|
||||||
import com.maddyhome.idea.vim.helper.noneOfEnum
|
import com.maddyhome.idea.vim.helper.noneOfEnum
|
||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
|
||||||
import com.maddyhome.idea.vim.key.MappingOwner
|
import com.maddyhome.idea.vim.key.MappingOwner
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||||
|
import com.maddyhome.idea.vim.ui.ex.ExEntryPanelService
|
||||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
||||||
@@ -68,8 +69,7 @@ object VimExtensionFacade {
|
|||||||
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated(
|
@Deprecated("Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||||
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
|
||||||
ReplaceWith(
|
ReplaceWith(
|
||||||
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||||
"com.maddyhome.idea.vim.VimPlugin"
|
"com.maddyhome.idea.vim.VimPlugin"
|
||||||
@@ -151,7 +151,7 @@ object VimExtensionFacade {
|
|||||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun inputKeyStroke(editor: Editor): KeyStroke {
|
fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||||
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
|
if (editor.vim.inRepeatMode) {
|
||||||
val input = Extension.consumeKeystroke()
|
val input = Extension.consumeKeystroke()
|
||||||
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
||||||
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
|
return input ?: error("Not enough keystrokes saved: ${Extension.lastExtensionHandler}")
|
||||||
@@ -183,19 +183,27 @@ object VimExtensionFacade {
|
|||||||
/** Returns a string typed in the input box similar to 'input()'. */
|
/** Returns a string typed in the input box similar to 'input()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||||
return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
return (injector.commandLine as ExEntryPanelService).inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@Deprecated("Please use com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister(com.maddyhome.idea.vim.api.VimEditor, char)")
|
||||||
fun getRegister(register: Char): List<KeyStroke>? {
|
fun getRegister(register: Char): List<KeyStroke>? {
|
||||||
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||||
|
@JvmStatic
|
||||||
|
fun getRegister(editor: VimEditor, register: Char): List<KeyStroke>? {
|
||||||
|
val reg = VimPlugin.getRegister().getRegister(editor, injector.executionContextManager.getEditorExecutionContext(editor), register) ?: return null
|
||||||
|
return reg.keys
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||||
val reg = injector.registerGroup.getRegister(register) ?: return null
|
val reg = caret.registerStorage.getRegister(register) ?: return null
|
||||||
return reg.keys
|
return reg.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +216,7 @@ object VimExtensionFacade {
|
|||||||
/** Set the current contents of the given register */
|
/** Set the current contents of the given register */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||||
injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current contents of the given register */
|
/** Set the current contents of the given register */
|
||||||
|
@@ -88,29 +88,10 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* During vim initialization process, it firstly loads the .vimrc file, then executes scripts from the plugins folder.
|
* See the docs for [VimExtension.init]
|
||||||
* This practically means that the .vimrc file is initialized first, then the plugins are loaded.
|
|
||||||
* See `:h initialization`
|
|
||||||
*
|
*
|
||||||
* In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect
|
* In IdeaVim we don't have a separate plugins folder to load it after .ideavimrc load. However, we can collect
|
||||||
* the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished.
|
* the list of plugins mentioned in the .ideavimrc and load them after .ideavimrc execution is finished.
|
||||||
*
|
|
||||||
* Why this matters? Because this affects the order of commands are executed. For example:
|
|
||||||
* ```
|
|
||||||
* plug 'tommcdo/vim-exchange'
|
|
||||||
* let g:exchange_no_mappings=1
|
|
||||||
* ```
|
|
||||||
* Here the user will expect that the exchange plugin won't have default mappings. However, if we load vim-exchange
|
|
||||||
* immediately, this variable won't be initialized at the moment of plugin initialization.
|
|
||||||
*
|
|
||||||
* There is also a tricky case for mappings override:
|
|
||||||
* ```
|
|
||||||
* plug 'tommcdo/vim-exchange'
|
|
||||||
* map X <Plug>(ExchangeLine)
|
|
||||||
* ```
|
|
||||||
* For this case, a plugin with a good implementation detects that there is already a defined mapping for
|
|
||||||
* `<Plug>(ExchangeLine)` and doesn't register the default cxx mapping. However, such detection requires the mapping
|
|
||||||
* to be defined before the plugin initialization.
|
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun enableDelayedExtensions() {
|
fun enableDelayedExtensions() {
|
||||||
|
@@ -33,7 +33,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
|
||||||
@@ -64,8 +63,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
*/
|
*/
|
||||||
private static class BracketPairs {
|
private static class BracketPairs {
|
||||||
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
|
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
|
||||||
@NotNull private final String openBrackets;
|
private final @NotNull String openBrackets;
|
||||||
@NotNull private final String closeBrackets;
|
private final @NotNull String closeBrackets;
|
||||||
|
|
||||||
static class ParseException extends Exception {
|
static class ParseException extends Exception {
|
||||||
public ParseException(@NotNull String message) {
|
public ParseException(@NotNull String message) {
|
||||||
@@ -87,8 +86,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
* @param bracketPairs comma-separated list of colon-separated bracket pairs.
|
* @param bracketPairs comma-separated list of colon-separated bracket pairs.
|
||||||
* @throws ParseException if a syntax error is detected.
|
* @throws ParseException if a syntax error is detected.
|
||||||
*/
|
*/
|
||||||
@NotNull
|
static @NotNull BracketPairs fromBracketPairList(final @NotNull String bracketPairs) throws ParseException {
|
||||||
static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseException {
|
|
||||||
StringBuilder openBrackets = new StringBuilder();
|
StringBuilder openBrackets = new StringBuilder();
|
||||||
StringBuilder closeBrackets = new StringBuilder();
|
StringBuilder closeBrackets = new StringBuilder();
|
||||||
ParseState state = ParseState.OPEN;
|
ParseState state = ParseState.OPEN;
|
||||||
@@ -128,7 +126,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
|
return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) {
|
BracketPairs(final @NotNull String openBrackets, final @NotNull String closeBrackets) {
|
||||||
assert openBrackets.length() == closeBrackets.length();
|
assert openBrackets.length() == closeBrackets.length();
|
||||||
this.openBrackets = openBrackets;
|
this.openBrackets = openBrackets;
|
||||||
this.closeBrackets = closeBrackets;
|
this.closeBrackets = closeBrackets;
|
||||||
@@ -158,10 +156,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
|
private static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
|
||||||
|
|
||||||
@Nullable
|
private static @Nullable String bracketPairsVariable() {
|
||||||
private static String bracketPairsVariable() {
|
|
||||||
final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
|
final Object value = VimPlugin.getVariableService().getGlobalVariableValue("argtextobj_pairs");
|
||||||
if (value instanceof VimString vimValue) {
|
if (value instanceof VimString vimValue) {
|
||||||
return vimValue.getValue();
|
return vimValue.getValue();
|
||||||
@@ -192,9 +189,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
this.isInner = isInner;
|
this.isInner = isInner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public TextRange getRange(@NotNull VimEditor editor,
|
public @Nullable TextRange getRange(@NotNull VimEditor editor,
|
||||||
@NotNull ImmutableVimCaret caret,
|
@NotNull ImmutableVimCaret caret,
|
||||||
@NotNull ExecutionContext context,
|
@NotNull ExecutionContext context,
|
||||||
int count,
|
int count,
|
||||||
@@ -236,24 +232,22 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
return new TextRange(finder.getLeftBound(), finder.getRightBound());
|
return new TextRange(finder.getLeftBound(), finder.getRightBound());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public TextObjectVisualType getVisualType() {
|
public @NotNull TextObjectVisualType getVisualType() {
|
||||||
return TextObjectVisualType.CHARACTER_WISE;
|
return TextObjectVisualType.CHARACTER_WISE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
|
||||||
|
|
||||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||||
//noinspection DuplicatedCode
|
//noinspection DuplicatedCode
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
|
||||||
|
int count0 = operatorArguments.getCount0();
|
||||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
final TextRange range = textObjectHandler.getRange(editor, caret, context, Math.max(1, count0), count0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||||
@@ -265,8 +259,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
|
||||||
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,9 +269,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
* position
|
* position
|
||||||
*/
|
*/
|
||||||
private static class ArgBoundsFinder {
|
private static class ArgBoundsFinder {
|
||||||
@NotNull private final CharSequence text;
|
private final @NotNull CharSequence text;
|
||||||
@NotNull private final Document document;
|
private final @NotNull Document document;
|
||||||
@NotNull private final BracketPairs brackets;
|
private final @NotNull BracketPairs brackets;
|
||||||
private int leftBound = Integer.MAX_VALUE;
|
private int leftBound = Integer.MAX_VALUE;
|
||||||
private int rightBound = Integer.MIN_VALUE;
|
private int rightBound = Integer.MIN_VALUE;
|
||||||
private int leftBracket;
|
private int leftBracket;
|
||||||
@@ -305,7 +298,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
* @param position starting position.
|
* @param position starting position.
|
||||||
*/
|
*/
|
||||||
boolean findBoundsAt(int position) throws IllegalStateException {
|
boolean findBoundsAt(int position) throws IllegalStateException {
|
||||||
if (text.length() == 0) {
|
if (text.isEmpty()) {
|
||||||
error = "empty document";
|
error = "empty document";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -25,9 +25,6 @@ import com.maddyhome.idea.vim.api.VimEditor
|
|||||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||||
import com.maddyhome.idea.vim.api.globalOptions
|
import com.maddyhome.idea.vim.api.globalOptions
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
|
||||||
import com.maddyhome.idea.vim.command.MappingMode
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||||
@@ -46,14 +43,12 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissin
|
|||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class CommentaryExtension : VimExtension {
|
internal class CommentaryExtension : VimExtension {
|
||||||
|
|
||||||
@@ -65,7 +60,7 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
selectionType: SelectionType,
|
selectionType: SelectionType,
|
||||||
resetCaret: Boolean = true,
|
resetCaret: Boolean = true,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val mode = editor.vimStateMachine.mode
|
val mode = editor.mode
|
||||||
if (mode !is Mode.VISUAL) {
|
if (mode !is Mode.VISUAL) {
|
||||||
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
editor.ij.selectionModel.setSelection(range.startOffset, range.endOffset)
|
||||||
}
|
}
|
||||||
@@ -80,11 +75,12 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
|
|
||||||
val project = editor.ij.project!!
|
val project = editor.ij.project!!
|
||||||
val callback = { afterCommenting(mode, editor, resetCaret, range) }
|
val callback = { afterCommenting(mode, editor, resetCaret, range) }
|
||||||
actions.any { executeActionWithCallbackOnSuccess(it, project, context, callback) }
|
actions.any { executeActionWithCallbackOnSuccess(editor, it, project, context, callback) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeActionWithCallbackOnSuccess(
|
private fun executeActionWithCallbackOnSuccess(
|
||||||
|
editor: VimEditor,
|
||||||
action: String,
|
action: String,
|
||||||
project: Project,
|
project: Project,
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
@@ -93,7 +89,7 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
val res = Ref.create<Boolean>(false)
|
val res = Ref.create<Boolean>(false)
|
||||||
AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
|
AsyncActionExecutionService.getInstance(project).withExecutionAfterAction(
|
||||||
action,
|
action,
|
||||||
{ res.set(injector.actionExecutor.executeAction(action, context)) },
|
{ res.set(injector.actionExecutor.executeAction(editor, name = action, context = context)) },
|
||||||
{ if (res.get()) callback() })
|
{ if (res.get()) callback() })
|
||||||
return res.get()
|
return res.get()
|
||||||
}
|
}
|
||||||
@@ -184,10 +180,8 @@ internal class CommentaryExtension : VimExtension {
|
|||||||
override val isRepeatable = true
|
override val isRepeatable = true
|
||||||
|
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
|
|
||||||
|
|
||||||
val keyState = KeyHandler.getInstance().keyHandlerState
|
val keyState = KeyHandler.getInstance().keyHandlerState
|
||||||
keyState.commandBuilder.completeCommandPart(Argument(command))
|
keyState.commandBuilder.addAction(CommentaryTextObjectMotionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -222,10 +222,10 @@ internal class VimExchangeExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val zRegText = getRegister('z')
|
val zRegText = getRegister(editor.vim, 'z')
|
||||||
val unnRegText = getRegister('"')
|
val unnRegText = getRegister(editor.vim, '"')
|
||||||
val startRegText = getRegister('*')
|
val startRegText = getRegister(editor.vim, '*')
|
||||||
val plusRegText = getRegister('+')
|
val plusRegText = getRegister(editor.vim, '+')
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
// TODO handle:
|
// TODO handle:
|
||||||
// " Compare using =~ because "'==' != 0" returns 0
|
// " Compare using =~ because "'==' != 0" returns 0
|
||||||
@@ -299,7 +299,7 @@ internal class VimExchangeExtension : VimExtension {
|
|||||||
|
|
||||||
private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
|
private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
|
||||||
// TODO: improve KeyStroke list to sting conversion
|
// TODO: improve KeyStroke list to sting conversion
|
||||||
fun getRegisterText(reg: Char): String = getRegister(reg)?.map { it.keyChar }?.joinToString("") ?: ""
|
fun getRegisterText(reg: Char): String = getRegister(editor.vim, reg)?.map { it.keyChar }?.joinToString("") ?: ""
|
||||||
fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
|
fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
|
||||||
val (startMark, endMark) =
|
val (startMark, endMark) =
|
||||||
if (isVisual) {
|
if (isVisual) {
|
||||||
@@ -313,9 +313,9 @@ internal class VimExchangeExtension : VimExtension {
|
|||||||
return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!)
|
return Pair(marks.getMark(vimEditor.primaryCaret(), startMark)!!, marks.getMark(vimEditor.primaryCaret(), endMark)!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
val unnRegText = getRegister('"')
|
val unnRegText = getRegister(editor.vim, '"')
|
||||||
val starRegText = getRegister('*')
|
val starRegText = getRegister(editor.vim, '*')
|
||||||
val plusRegText = getRegister('+')
|
val plusRegText = getRegister(editor.vim, '+')
|
||||||
|
|
||||||
val (selectionStart, selectionEnd) = getMarks(isVisual)
|
val (selectionStart, selectionEnd) = getMarks(isVisual)
|
||||||
if (isVisual) {
|
if (isVisual) {
|
||||||
|
@@ -23,13 +23,15 @@ import com.intellij.util.Alarm
|
|||||||
import com.intellij.util.Alarm.ThreadToUse
|
import com.intellij.util.Alarm.ThreadToUse
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
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.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
||||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
|
||||||
import com.maddyhome.idea.vim.listener.VimYankListener
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
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.VimDataType
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
@@ -43,6 +45,10 @@ private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_durati
|
|||||||
|
|
||||||
@NonNls
|
@NonNls
|
||||||
private val HIGHLIGHT_COLOR_VARIABLE_NAME = "highlightedyank_highlight_color"
|
private val HIGHLIGHT_COLOR_VARIABLE_NAME = "highlightedyank_highlight_color"
|
||||||
|
|
||||||
|
@NonNls
|
||||||
|
private val HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME = "highlightedyank_highlight_foreground_color"
|
||||||
|
|
||||||
private var defaultHighlightTextColor: Color? = null
|
private var defaultHighlightTextColor: Color? = null
|
||||||
|
|
||||||
private fun getDefaultHighlightTextColor(): Color {
|
private fun getDefaultHighlightTextColor(): Color {
|
||||||
@@ -73,9 +79,12 @@ internal class HighlightColorResetter : LafManagerListener {
|
|||||||
* if you want to change background color of highlight you can provide the rgba of the color you want e.g.
|
* if you want to change background color of highlight you can provide the rgba of the color you want e.g.
|
||||||
* let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"
|
* let g:highlightedyank_highlight_color = "rgba(160, 160, 160, 155)"
|
||||||
*
|
*
|
||||||
|
* if you want to change text color of highlight you can provide the rgba of the color you want e.g.
|
||||||
|
* let g:highlightedyank_highlight_foreground_color = "rgba(0, 0, 0, 255)"
|
||||||
|
*
|
||||||
* When a new text is yanked or user starts editing, the old highlighting would be deleted.
|
* 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 val highlightHandler = HighlightHandler()
|
||||||
private var initialised = false
|
private var initialised = false
|
||||||
|
|
||||||
@@ -83,8 +92,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
|
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
|
||||||
VimPlugin.getYank().addListener(this)
|
injector.listenersNotifier.modeChangeListeners.add(this)
|
||||||
VimPlugin.getChange().addInsertListener(this)
|
injector.listenersNotifier.yankListeners.add(this)
|
||||||
|
|
||||||
// Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
|
// 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
|
// IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
|
||||||
@@ -105,8 +114,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
|
|
||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
|
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
|
||||||
VimPlugin.getYank().removeListener(this)
|
injector.listenersNotifier.modeChangeListeners.remove(this)
|
||||||
VimPlugin.getChange().removeInsertListener(this)
|
injector.listenersNotifier.yankListeners.remove(this)
|
||||||
|
|
||||||
highlightHandler.clearYankHighlighters()
|
highlightHandler.clearYankHighlighters()
|
||||||
initialised = false
|
initialised = false
|
||||||
@@ -117,7 +126,8 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
highlightHandler.highlightYankRange(editor.ij, range)
|
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()
|
ensureInitialised()
|
||||||
highlightHandler.clearYankHighlighters()
|
highlightHandler.clearYankHighlighters()
|
||||||
}
|
}
|
||||||
@@ -178,13 +188,15 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
highlighters.clear()
|
highlighters.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHighlightTextAttributes(editor: Editor) = TextAttributes(
|
private fun getHighlightTextAttributes(editor: Editor): TextAttributes {
|
||||||
null,
|
return TextAttributes(
|
||||||
|
extractUserHighlightForegroundColor(),
|
||||||
extractUsersHighlightColor(),
|
extractUsersHighlightColor(),
|
||||||
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
|
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
|
||||||
EffectType.SEARCH_MATCH,
|
EffectType.SEARCH_MATCH,
|
||||||
Font.PLAIN,
|
Font.PLAIN,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun extractUsersHighlightDuration(): Int {
|
private fun extractUsersHighlightDuration(): Int {
|
||||||
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
|
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
|
||||||
@@ -197,15 +209,52 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun extractUsersHighlightColor(): Color {
|
private fun extractUsersHighlightColor(): Color {
|
||||||
return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
|
val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_COLOR_VARIABLE_NAME)
|
||||||
val rgba = value.asString()
|
if (value != null) {
|
||||||
|
return try {
|
||||||
|
parseRgbaColor(value.asString())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
@VimNlsSafe val message = MessageHelper.message(
|
||||||
|
"highlightedyank.invalid.value.of.0.1",
|
||||||
|
"g:$HIGHLIGHT_COLOR_VARIABLE_NAME",
|
||||||
|
e.message ?: "",
|
||||||
|
)
|
||||||
|
VimPlugin.showMessage(message)
|
||||||
|
getDefaultHighlightTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getDefaultHighlightTextColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractUserHighlightForegroundColor(): Color? {
|
||||||
|
val value = VimPlugin.getVariableService().getGlobalVariableValue(HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return try {
|
||||||
|
parseRgbaColor(value.asString())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
@VimNlsSafe val message = MessageHelper.message(
|
||||||
|
"highlightedyank.invalid.value.of.0.1",
|
||||||
|
"g:$HIGHLIGHT_FOREGROUND_COLOR_VARIABLE_NAME",
|
||||||
|
e.message ?: "",
|
||||||
|
)
|
||||||
|
VimPlugin.showMessage(message)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseRgbaColor(colorString: String): Color {
|
||||||
|
val rgba = colorString
|
||||||
.substring(4)
|
.substring(4)
|
||||||
.filter { it != '(' && it != ')' && !it.isWhitespace() }
|
.filter { it != '(' && it != ')' && !it.isWhitespace() }
|
||||||
.split(',')
|
.split(',')
|
||||||
.map { it.toInt() }
|
.map { it.toInt() }
|
||||||
|
|
||||||
Color(rgba[0], rgba[1], rgba[2], rgba[3])
|
if (rgba.size != 4 || rgba.any { it < 0 || it > 255 }) {
|
||||||
|
throw IllegalArgumentException("Invalid RGBA values. Each component must be between 0 and 255")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Color(rgba[0], rgba[1], rgba[2], rgba[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
|
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
|
||||||
|
@@ -44,6 +44,7 @@ import com.maddyhome.idea.vim.helper.enumSetOf
|
|||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@@ -93,34 +94,29 @@ internal class Matchit : VimExtension {
|
|||||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||||
val keyHandler = KeyHandler.getInstance()
|
val keyHandler = KeyHandler.getInstance()
|
||||||
val keyState = keyHandler.keyHandlerState
|
val keyState = keyHandler.keyHandlerState
|
||||||
val count = keyState.commandBuilder.count
|
|
||||||
|
|
||||||
// Reset the command count so it doesn't transfer onto subsequent commands.
|
// Reset the command count so it doesn't transfer onto subsequent commands.
|
||||||
keyState.commandBuilder.resetCount()
|
keyState.commandBuilder.resetCount()
|
||||||
|
|
||||||
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
||||||
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
||||||
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState)
|
if (editor.mode is Mode.OP_PENDING) {
|
||||||
|
|
||||||
if (isInOpPending) {
|
|
||||||
val matchitAction = MatchitAction()
|
val matchitAction = MatchitAction()
|
||||||
matchitAction.reverse = reverse
|
matchitAction.reverse = reverse
|
||||||
matchitAction.isInOpPending = true
|
matchitAction.isInOpPending = true
|
||||||
|
|
||||||
keyState.commandBuilder.completeCommandPart(
|
keyState.commandBuilder.addAction(matchitAction)
|
||||||
Argument(
|
|
||||||
Command(
|
|
||||||
count,
|
|
||||||
matchitAction,
|
|
||||||
Command.Type.MOTION,
|
|
||||||
EnumSet.noneOf(CommandFlags::class.java),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
editor.sortedCarets().forEach { caret ->
|
editor.sortedCarets().forEach { caret ->
|
||||||
injector.jumpService.saveJumpLocation(editor)
|
injector.jumpService.saveJumpLocation(editor)
|
||||||
caret.moveToOffset(getMatchitOffset(editor.ij, caret.ij, count, isInOpPending, reverse))
|
caret.moveToOffset(
|
||||||
|
getMatchitOffset(
|
||||||
|
editor.ij,
|
||||||
|
caret.ij,
|
||||||
|
operatorArguments.count0,
|
||||||
|
isInOpPending = false,
|
||||||
|
reverse
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +350,7 @@ private object FileTypePatterns {
|
|||||||
|
|
||||||
private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}')
|
private val DEFAULT_PAIRS = setOf('(', ')', '[', ']', '{', '}')
|
||||||
|
|
||||||
private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPending: Boolean, reverse: Boolean): Int {
|
private fun getMatchitOffset(editor: Editor, caret: Caret, count0: Int, isInOpPending: Boolean, reverse: Boolean): Int {
|
||||||
val virtualFile = EditorHelper.getVirtualFile(editor)
|
val virtualFile = EditorHelper.getVirtualFile(editor)
|
||||||
var caretOffset = caret.offset
|
var caretOffset = caret.offset
|
||||||
|
|
||||||
@@ -367,9 +363,9 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPen
|
|||||||
val currentChar = editor.document.charsSequence[caretOffset]
|
val currentChar = editor.document.charsSequence[caretOffset]
|
||||||
var motionOffset: Int? = null
|
var motionOffset: Int? = null
|
||||||
|
|
||||||
if (count > 0) {
|
if (count0 > 0) {
|
||||||
// Matchit doesn't affect the percent motion, so we fall back to the default behavior.
|
// Matchit doesn't affect the percent motion, so we fall back to the default behavior.
|
||||||
motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count)
|
motionOffset = VimPlugin.getMotion().moveCaretToLinePercent(editor.vim, caret.vim, count0)
|
||||||
} else {
|
} else {
|
||||||
// Check the simplest case first.
|
// Check the simplest case first.
|
||||||
if (DEFAULT_PAIRS.contains(currentChar)) {
|
if (DEFAULT_PAIRS.contains(currentChar)) {
|
||||||
@@ -400,8 +396,7 @@ private fun getMatchitOffset(editor: Editor, caret: Caret, count: Int, isInOpPen
|
|||||||
|
|
||||||
private fun getMotionOffset(motion: Motion): Int? {
|
private fun getMotionOffset(motion: Motion): Int? {
|
||||||
return when (motion) {
|
return when (motion) {
|
||||||
is Motion.AbsoluteOffset -> motion.offset
|
is Motion.AdjustedOffset, is Motion.AbsoluteOffset -> motion.offset
|
||||||
is Motion.AdjustedOffset -> motion.offset
|
|
||||||
is Motion.Error, is Motion.NoMotion -> null
|
is Motion.Error, is Motion.NoMotion -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,13 +42,10 @@ import com.maddyhome.idea.vim.extension.VimExtension
|
|||||||
import com.maddyhome.idea.vim.group.KeyGroup
|
import com.maddyhome.idea.vim.group.KeyGroup
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.helper.runAfterGotFocus
|
import com.maddyhome.idea.vim.helper.runAfterGotFocus
|
||||||
import com.maddyhome.idea.vim.key.CommandNode
|
import com.maddyhome.idea.vim.key.KeyStrokeTrie
|
||||||
import com.maddyhome.idea.vim.key.CommandPartNode
|
|
||||||
import com.maddyhome.idea.vim.key.MappingOwner
|
import com.maddyhome.idea.vim.key.MappingOwner
|
||||||
import com.maddyhome.idea.vim.key.Node
|
|
||||||
import com.maddyhome.idea.vim.key.RequiredShortcut
|
import com.maddyhome.idea.vim.key.RequiredShortcut
|
||||||
import com.maddyhome.idea.vim.key.RootNode
|
import com.maddyhome.idea.vim.key.add
|
||||||
import com.maddyhome.idea.vim.key.addLeafs
|
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||||
@@ -198,6 +195,8 @@ internal class NerdTree : VimExtension {
|
|||||||
internal var waitForSearch = false
|
internal var waitForSearch = false
|
||||||
internal var speedSearchListenerInstalled = false
|
internal var speedSearchListenerInstalled = false
|
||||||
|
|
||||||
|
private val keys = mutableListOf<KeyStroke>()
|
||||||
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
var keyStroke = getKeyStroke(e) ?: return
|
var keyStroke = getKeyStroke(e) ?: return
|
||||||
val keyChar = keyStroke.keyChar
|
val keyChar = keyStroke.keyChar
|
||||||
@@ -205,20 +204,14 @@ internal class NerdTree : VimExtension {
|
|||||||
keyStroke = KeyStroke.getKeyStroke(keyChar)
|
keyStroke = KeyStroke.getKeyStroke(keyChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextNode = currentNode[keyStroke]
|
keys.add(keyStroke)
|
||||||
|
actionsRoot.getData(keys)?.let { action ->
|
||||||
when (nextNode) {
|
|
||||||
null -> currentNode = actionsRoot
|
|
||||||
is CommandNode<NerdAction> -> {
|
|
||||||
currentNode = actionsRoot
|
|
||||||
|
|
||||||
val action = nextNode.actionHolder
|
|
||||||
when (action) {
|
when (action) {
|
||||||
is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
|
is NerdAction.ToIj -> Util.callAction(null, action.name, e.dataContext.vim)
|
||||||
is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
|
is NerdAction.Code -> e.project?.let { action.action(it, e.dataContext, e) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
is CommandPartNode<NerdAction> -> currentNode = nextNode
|
keys.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,6 +474,9 @@ internal class NerdTree : VimExtension {
|
|||||||
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
|
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
|
||||||
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
|
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
|
||||||
registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
|
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("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
|
||||||
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
||||||
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
||||||
@@ -537,37 +533,29 @@ private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
|||||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
|
private fun registerCommand(variable: String, defaultMapping: String, action: NerdAction) {
|
||||||
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||||
val mappings = if (variableValue is VimString) {
|
val mapping = if (variableValue is VimString) {
|
||||||
variableValue.value
|
variableValue.value
|
||||||
} else {
|
} else {
|
||||||
default
|
defaultMapping
|
||||||
}
|
}
|
||||||
actionsRoot.addLeafs(mappings, action)
|
registerCommand(mapping, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerCommand(default: String, action: NerdAction) {
|
private fun registerCommand(mapping: String, action: NerdAction) {
|
||||||
actionsRoot.addLeafs(default, action)
|
actionsRoot.add(mapping, action)
|
||||||
|
injector.parser.parseKeys(mapping).forEach {
|
||||||
|
distinctShortcuts.add(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val actionsRoot: KeyStrokeTrie<NerdAction> = KeyStrokeTrie<NerdAction>("NERDTree")
|
||||||
private val actionsRoot: RootNode<NerdAction> = RootNode()
|
private val distinctShortcuts = mutableSetOf<KeyStroke>()
|
||||||
private var currentNode: CommandPartNode<NerdAction> = actionsRoot
|
|
||||||
private fun collectShortcuts(node: Node<NerdAction>): Set<KeyStroke> {
|
|
||||||
return if (node is CommandPartNode<NerdAction>) {
|
|
||||||
val res = node.keys.toMutableSet()
|
|
||||||
res += node.values.map { collectShortcuts(it) }.flatten()
|
|
||||||
res
|
|
||||||
} else {
|
|
||||||
emptySet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun installDispatcher(project: Project) {
|
private fun installDispatcher(project: Project) {
|
||||||
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
||||||
val shortcuts =
|
val shortcuts = distinctShortcuts.map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
||||||
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
|
||||||
dispatcher.registerCustomShortcutSet(
|
dispatcher.registerCustomShortcutSet(
|
||||||
KeyGroup.toShortcutSet(shortcuts),
|
KeyGroup.toShortcutSet(shortcuts),
|
||||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
||||||
|
@@ -62,7 +62,7 @@ internal class ParagraphMotion : VimExtension {
|
|||||||
toKeys: List<KeyStroke>,
|
toKeys: List<KeyStroke>,
|
||||||
recursive: Boolean,
|
recursive: Boolean,
|
||||||
) {
|
) {
|
||||||
val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().hasmapfrom(it, fromKeys) }
|
val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().getKeyMapping(it).getLayer(fromKeys) != null }
|
||||||
putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
|
putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.extension.replacewithregister
|
|||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||||
@@ -144,7 +143,7 @@ internal class ReplaceWithRegister : VimExtension {
|
|||||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||||
val registerGroup = injector.registerGroup
|
val registerGroup = injector.registerGroup
|
||||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||||
val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
|
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||||
|
|
||||||
var usedType = savedRegister.type
|
var usedType = savedRegister.type
|
||||||
var usedText = savedRegister.text
|
var usedText = savedRegister.text
|
||||||
@@ -166,17 +165,11 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
|
|||||||
putToLine = -1,
|
putToLine = -1,
|
||||||
)
|
)
|
||||||
val vimEditor = editor.vim
|
val vimEditor = editor.vim
|
||||||
val keyHandler = KeyHandler.getInstance()
|
|
||||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||||
VimPlugin.getPut().putText(
|
VimPlugin.getPut().putText(
|
||||||
vimEditor,
|
vimEditor,
|
||||||
context.vim,
|
context.vim,
|
||||||
putData,
|
putData,
|
||||||
operatorArguments = OperatorArguments(
|
|
||||||
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
|
|
||||||
0,
|
|
||||||
editor.vim.mode,
|
|
||||||
),
|
|
||||||
saveToRegister = false
|
saveToRegister = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
|||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||||
import com.maddyhome.idea.vim.helper.StrictMode
|
import com.maddyhome.idea.vim.helper.StrictMode
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -45,15 +46,26 @@ private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK = 300
|
|||||||
// By [Mikhail Levchenko](https://github.com/Mishkun)
|
// By [Mikhail Levchenko](https://github.com/Mishkun)
|
||||||
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
||||||
internal class IdeaVimSneakExtension : VimExtension {
|
internal class IdeaVimSneakExtension : VimExtension {
|
||||||
|
@Suppress("CompanionObjectInExtension")
|
||||||
|
companion object {
|
||||||
|
private var highlightHandler: HighlightHandler? = null
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
internal fun stopTimer() {
|
||||||
|
highlightHandler?.stopExistingTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getName(): String = "sneak"
|
override fun getName(): String = "sneak"
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
val highlightHandler = HighlightHandler()
|
val _highlightHandler = HighlightHandler()
|
||||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
|
highlightHandler = _highlightHandler
|
||||||
|
mapToFunctionAndProvideKeys("s", SneakHandler(_highlightHandler, Direction.FORWARD), MappingMode.NXO)
|
||||||
|
|
||||||
// vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
|
// vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
|
||||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.NO)
|
mapToFunctionAndProvideKeys("S", SneakHandler(_highlightHandler, Direction.BACKWARD), MappingMode.NO)
|
||||||
mapToFunctionAndProvideKeys("Z", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.X)
|
mapToFunctionAndProvideKeys("Z", SneakHandler(_highlightHandler, Direction.BACKWARD), MappingMode.X)
|
||||||
|
|
||||||
// workaround to support ; and , commands
|
// workaround to support ; and , commands
|
||||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
|
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
|
||||||
@@ -61,8 +73,8 @@ internal class IdeaVimSneakExtension : VimExtension {
|
|||||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
|
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
|
||||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
|
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
|
||||||
|
|
||||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
|
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(_highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
|
||||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(_highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SneakHandler(
|
private class SneakHandler(
|
||||||
@@ -209,6 +221,7 @@ internal class IdeaVimSneakExtension : VimExtension {
|
|||||||
private class HighlightHandler {
|
private class HighlightHandler {
|
||||||
private var editor: Editor? = null
|
private var editor: Editor? = null
|
||||||
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
||||||
|
private var timer: Timer? = null
|
||||||
|
|
||||||
fun highlightSneakRange(editor: Editor, range: TextRange) {
|
fun highlightSneakRange(editor: Editor, range: TextRange) {
|
||||||
clearAllSneakHighlighters()
|
clearAllSneakHighlighters()
|
||||||
@@ -254,13 +267,19 @@ internal class IdeaVimSneakExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
||||||
val timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
|
stopExistingTimer()
|
||||||
|
timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
|
||||||
if (editor?.isDisposed != true) {
|
if (editor?.isDisposed != true) {
|
||||||
editor?.markupModel?.removeHighlighter(highlighter)
|
editor?.markupModel?.removeHighlighter(highlighter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer.isRepeats = false
|
timer?.isRepeats = false
|
||||||
timer.start()
|
timer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopExistingTimer() {
|
||||||
|
timer?.stop()
|
||||||
|
timer?.actionListeners?.forEach { it.actionPerformed(null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHighlightTextAttributes() = TextAttributes(
|
private fun getHighlightTextAttributes() = TextAttributes(
|
||||||
@@ -307,7 +326,7 @@ private fun VimExtension.mapToFunctionAndProvideKeys(
|
|||||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
||||||
}
|
}
|
||||||
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
||||||
injector.keyGroup.hasmapfrom(it, fromKeys)
|
injector.keyGroup.getKeyMapping(it).getLayer(fromKeys) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
val doubleFiltered = mappingModes
|
val doubleFiltered = mappingModes
|
||||||
|
@@ -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.VimExtensionFacade.setRegisterForCaret
|
||||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||||
import com.maddyhome.idea.vim.group.findBlockRange
|
import com.maddyhome.idea.vim.group.findBlockRange
|
||||||
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
@@ -46,6 +47,7 @@ import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
|||||||
import com.maddyhome.idea.vim.put.PutData
|
import com.maddyhome.idea.vim.put.PutData
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
|
import com.maddyhome.idea.vim.state.mode.returnTo
|
||||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||||
import org.jetbrains.annotations.NonNls
|
import org.jetbrains.annotations.NonNls
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
@@ -115,6 +117,9 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
// it.moveToOffset(lineStartOffset)
|
// it.moveToOffset(lineStartOffset)
|
||||||
}
|
}
|
||||||
// Jump back to start
|
// Jump back to start
|
||||||
|
if (editor.mode !is Mode.NORMAL) {
|
||||||
|
editor.mode = Mode.NORMAL()
|
||||||
|
}
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +141,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
// Leave visual mode
|
// Leave visual mode
|
||||||
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
editor.exitVisualMode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,17 +161,17 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
|
fun change(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
|
||||||
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
|
editor.ij.runWithEveryCaretAndRestore { changeAtCaret(editor, context, charFrom, newSurround) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: Pair<String, String>?) {
|
fun changeAtCaret(editor: VimEditor, context: ExecutionContext, charFrom: Char, newSurround: SurroundPair?) {
|
||||||
// Save old register values for carets
|
// Save old register values for carets
|
||||||
val surroundings = editor.sortedCarets()
|
val surroundings = editor.sortedCarets()
|
||||||
.map {
|
.map {
|
||||||
val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it)
|
val oldValue: List<KeyStroke>? = getRegisterForCaret(REGISTER, it)
|
||||||
setRegisterForCaret(REGISTER, it, null)
|
setRegisterForCaret(REGISTER, it, null)
|
||||||
SurroundingInfo(it, null, oldValue)
|
SurroundingInfo(it, null, oldValue, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete surrounding's content
|
// Delete surrounding's content
|
||||||
@@ -182,21 +187,25 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val registerValue = getRegisterForCaret(REGISTER, it.caret)
|
val registerValue = getRegisterForCaret(REGISTER, it.caret)
|
||||||
val innerValue = if (registerValue.isNullOrEmpty()) null else registerValue
|
val innerValue = if (registerValue.isNullOrEmpty()) emptyList() else registerValue
|
||||||
it.innerText = innerValue
|
it.innerText = innerValue
|
||||||
}
|
|
||||||
|
|
||||||
surroundings.forEach {
|
// Valid surroundings are only those that:
|
||||||
if (it.innerText == null && getRegisterForCaret(REGISTER, it.caret)?.isNotEmpty() == true) {
|
// - are validly wrapping with surround characters (i.e. parenthesis, brackets, tags, quotes, etc.);
|
||||||
it.innerText = emptyList()
|
// - or have non-empty inner text (e.g. when we are surrounding words: `csw"`)
|
||||||
|
if (currentSurrounding != null || innerValue.isNotEmpty()) {
|
||||||
|
it.isValidSurrounding = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
surroundings
|
surroundings
|
||||||
.filter { it.innerText != null } // we do nothing with carets that are not inside the surrounding
|
.filter { it.isValidSurrounding } // we do nothing with carets that are not inside the surrounding
|
||||||
.map { surrounding ->
|
.map { surrounding ->
|
||||||
val innerValue = injector.parser.toPrintableString(surrounding.innerText!!)
|
val innerValue = injector.parser.toPrintableString(surrounding.innerText!!)
|
||||||
val text = newSurround?.let { it.first + innerValue + it.second } ?: innerValue
|
val text = newSurround?.let {
|
||||||
|
val trimmedValue = if (newSurround.shouldTrim) innerValue.trim() else innerValue
|
||||||
|
it.first + trimmedValue + it.second
|
||||||
|
} ?: innerValue
|
||||||
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
|
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
|
||||||
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
|
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
|
||||||
|
|
||||||
@@ -249,7 +258,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?) {
|
private data class SurroundingInfo(val caret: VimCaret, var innerText: List<KeyStroke>?, val oldRegisterContent: List<KeyStroke>?, var isValidSurrounding: Boolean) {
|
||||||
fun restoreRegister() {
|
fun restoreRegister() {
|
||||||
setRegisterForCaret(REGISTER, caret, oldRegisterContent)
|
setRegisterForCaret(REGISTER, caret, oldRegisterContent)
|
||||||
}
|
}
|
||||||
@@ -292,7 +301,7 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: Pair<String, String>, count: Int) {
|
private fun applyOnce(editor: Editor, change: VimChangeGroup, pair: SurroundPair, count: Int) {
|
||||||
// XXX: Will it work with line-wise or block-wise selections?
|
// XXX: Will it work with line-wise or block-wise selections?
|
||||||
val primaryCaret = editor.caretModel.primaryCaret
|
val primaryCaret = editor.caretModel.primaryCaret
|
||||||
val range = getSurroundRange(primaryCaret.vim)
|
val range = getSurroundRange(primaryCaret.vim)
|
||||||
@@ -306,8 +315,10 @@ internal class VimSurroundExtension : VimExtension {
|
|||||||
|
|
||||||
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
private fun getSurroundRange(caret: VimCaret): TextRange? {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
val ijEditor = editor.ij
|
if (editor.mode is Mode.CMD_LINE) {
|
||||||
return when (ijEditor.vim.mode) {
|
editor.mode = (editor.mode as Mode.CMD_LINE).returnTo()
|
||||||
|
}
|
||||||
|
return when (editor.mode) {
|
||||||
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
|
is Mode.NORMAL -> injector.markService.getChangeMarks(caret)
|
||||||
is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
|
is Mode.VISUAL -> caret.run { TextRange(selectionStart, selectionEnd) }
|
||||||
else -> null
|
else -> null
|
||||||
@@ -324,37 +335,42 @@ private const val OPERATOR_FUNC = "SurroundOperatorFunc"
|
|||||||
|
|
||||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||||
|
|
||||||
|
private data class SurroundPair(val first: String, val second: String, val shouldTrim: Boolean)
|
||||||
|
|
||||||
private val SURROUND_PAIRS = mapOf(
|
private val SURROUND_PAIRS = mapOf(
|
||||||
'b' to ("(" to ")"),
|
'b' to SurroundPair("(", ")", false),
|
||||||
'(' to ("( " to " )"),
|
'(' to SurroundPair("( ", " )", false),
|
||||||
')' to ("(" to ")"),
|
')' to SurroundPair("(", ")", true),
|
||||||
'B' to ("{" to "}"),
|
'B' to SurroundPair("{", "}", false),
|
||||||
'{' to ("{ " to " }"),
|
'{' to SurroundPair("{ ", " }", false),
|
||||||
'}' to ("{" to "}"),
|
'}' to SurroundPair("{", "}", true),
|
||||||
'r' to ("[" to "]"),
|
'r' to SurroundPair("[", "]", false),
|
||||||
'[' to ("[ " to " ]"),
|
'[' to SurroundPair("[ ", " ]", false),
|
||||||
']' to ("[" to "]"),
|
']' to SurroundPair("[", "]", true),
|
||||||
'a' to ("<" to ">"),
|
'a' to SurroundPair("<", ">", false),
|
||||||
'>' to ("<" to ">"),
|
'>' to SurroundPair("<", ">", false),
|
||||||
's' to (" " to ""),
|
's' to SurroundPair(" ", "", false),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_PAIRS) {
|
private fun getSurroundPair(c: Char): SurroundPair? = if (c in SURROUND_PAIRS) {
|
||||||
SURROUND_PAIRS[c]
|
SURROUND_PAIRS[c]
|
||||||
} else if (!c.isLetter()) {
|
} else if (!c.isLetter()) {
|
||||||
val s = c.toString()
|
val s = c.toString()
|
||||||
s to s
|
SurroundPair(s, s, false)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
private fun inputTagPair(editor: Editor, context: DataContext): SurroundPair? {
|
||||||
val tagInput = inputString(editor, context, "<", '>')
|
val tagInput = inputString(editor, context, "<", '>')
|
||||||
|
if (editor.vim.mode is Mode.CMD_LINE) {
|
||||||
|
editor.vim.mode = editor.vim.mode.returnTo()
|
||||||
|
}
|
||||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||||
return if (matcher.find()) {
|
return if (matcher.find()) {
|
||||||
val tagName = matcher.group(1)
|
val tagName = matcher.group(1)
|
||||||
val tagAttributes = matcher.group(2)
|
val tagAttributes = matcher.group(2)
|
||||||
"<$tagName$tagAttributes>" to "</$tagName>"
|
SurroundPair("<$tagName$tagAttributes>", "</$tagName>", false)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -364,13 +380,20 @@ private fun inputFunctionName(
|
|||||||
editor: Editor,
|
editor: Editor,
|
||||||
context: DataContext,
|
context: DataContext,
|
||||||
withInternalSpaces: Boolean,
|
withInternalSpaces: Boolean,
|
||||||
): Pair<String, String>? {
|
): SurroundPair? {
|
||||||
val functionNameInput = inputString(editor, context, "function: ", null)
|
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
|
if (functionNameInput.isEmpty()) return null
|
||||||
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
return if (withInternalSpaces) {
|
||||||
|
SurroundPair("$functionNameInput( ", " )", false)
|
||||||
|
} else {
|
||||||
|
SurroundPair("$functionNameInput(", ")", false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
|
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): SurroundPair? = when (c) {
|
||||||
'<', 't' -> inputTagPair(editor, context)
|
'<', 't' -> inputTagPair(editor, context)
|
||||||
'f' -> inputFunctionName(editor, context, false)
|
'f' -> inputFunctionName(editor, context, false)
|
||||||
'F' -> inputFunctionName(editor, context, true)
|
'F' -> inputFunctionName(editor, context, true)
|
||||||
@@ -389,7 +412,7 @@ private fun getChar(editor: Editor): Char {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performSurround(pair: Pair<String, String>, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
|
private fun performSurround(pair: SurroundPair, range: TextRange, caret: VimCaret, count: Int, tagsOnNewLines: Boolean = false) {
|
||||||
runWriteAction {
|
runWriteAction {
|
||||||
val editor = caret.editor
|
val editor = caret.editor
|
||||||
val change = VimPlugin.getChange()
|
val change = VimPlugin.getChange()
|
||||||
|
@@ -29,14 +29,12 @@ import com.maddyhome.idea.vim.state.mode.Mode;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Port of vim-entire:
|
* Port of vim-entire:
|
||||||
* https://github.com/kana/vim-textobj-entire
|
* <a href="https://github.com/kana/vim-textobj-entire">vim-textobj-entire</a>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* vim-textobj-entire provides two text objects:
|
* vim-textobj-entire provides two text objects:
|
||||||
@@ -51,7 +49,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingI
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* See also the reference manual for more details at:
|
* See also the reference manual for more details at:
|
||||||
* https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
|
* <a href="https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt">text-obj-entire.txt</a>
|
||||||
*
|
*
|
||||||
* @author Alexandre Grison (@agrison)
|
* @author Alexandre Grison (@agrison)
|
||||||
*/
|
*/
|
||||||
@@ -94,9 +92,8 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
|
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public TextRange getRange(@NotNull VimEditor editor,
|
public @Nullable TextRange getRange(@NotNull VimEditor editor,
|
||||||
@NotNull ImmutableVimCaret caret,
|
@NotNull ImmutableVimCaret caret,
|
||||||
@NotNull ExecutionContext context,
|
@NotNull ExecutionContext context,
|
||||||
int count,
|
int count,
|
||||||
@@ -125,24 +122,22 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
return new TextRange(start, end);
|
return new TextRange(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public TextObjectVisualType getVisualType() {
|
public @NotNull TextObjectVisualType getVisualType() {
|
||||||
return TextObjectVisualType.CHARACTER_WISE;
|
return TextObjectVisualType.CHARACTER_WISE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
|
||||||
|
|
||||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||||
//noinspection DuplicatedCode
|
//noinspection DuplicatedCode
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
|
||||||
|
int count0 = operatorArguments.getCount0();
|
||||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||||
@@ -155,9 +150,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
|
||||||
textObjectHandler, Command.Type.MOTION,
|
|
||||||
EnumSet.noneOf(CommandFlags.class))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,14 +30,12 @@ import com.maddyhome.idea.vim.state.mode.Mode;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||||
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Port of vim-indent-object:
|
* Port of vim-indent-object:
|
||||||
* https://github.com/michaeljsmith/vim-indent-object
|
* <a href="https://github.com/michaeljsmith/vim-indent-object">vim-indent-object</a>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* vim-indent-object provides these text objects based on the cursor line's indentation:
|
* vim-indent-object provides these text objects based on the cursor line's indentation:
|
||||||
@@ -49,7 +47,7 @@ import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* See also the reference manual for more details at:
|
* See also the reference manual for more details at:
|
||||||
* https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt
|
* <a href="https://github.com/michaeljsmith/vim-indent-object/blob/master/doc/indent-object.txt">indent-object.txt</a>
|
||||||
*
|
*
|
||||||
* @author Shrikant Kandula (@sharat87)
|
* @author Shrikant Kandula (@sharat87)
|
||||||
*/
|
*/
|
||||||
@@ -98,9 +96,8 @@ public class VimIndentObject implements VimExtension {
|
|||||||
this.includeBelow = includeBelow;
|
this.includeBelow = includeBelow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public TextRange getRange(@NotNull VimEditor editor,
|
public @Nullable TextRange getRange(@NotNull VimEditor editor,
|
||||||
@NotNull ImmutableVimCaret caret,
|
@NotNull ImmutableVimCaret caret,
|
||||||
@NotNull ExecutionContext context,
|
@NotNull ExecutionContext context,
|
||||||
int count,
|
int count,
|
||||||
@@ -249,9 +246,8 @@ public class VimIndentObject implements VimExtension {
|
|||||||
return new TextRange(upperBoundaryOffset, lowerBoundaryOffset);
|
return new TextRange(upperBoundaryOffset, lowerBoundaryOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public TextObjectVisualType getVisualType() {
|
public @NotNull TextObjectVisualType getVisualType() {
|
||||||
return TextObjectVisualType.LINE_WISE;
|
return TextObjectVisualType.LINE_WISE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,15 +260,14 @@ public class VimIndentObject implements VimExtension {
|
|||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||||
IjVimEditor vimEditor = (IjVimEditor)editor;
|
IjVimEditor vimEditor = (IjVimEditor)editor;
|
||||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
|
||||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
|
||||||
|
|
||||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||||
|
|
||||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
if (!(editor.getMode() instanceof Mode.OP_PENDING)) {
|
||||||
|
int count0 = operatorArguments.getCount0();
|
||||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, Math.max(1, count0), count0);
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||||
@@ -285,9 +280,7 @@ public class VimIndentObject implements VimExtension {
|
|||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
keyHandlerState.getCommandBuilder().addAction(textObjectHandler);
|
||||||
textObjectHandler, Command.Type.MOTION,
|
|
||||||
EnumSet.noneOf(CommandFlags.class))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,77 +15,38 @@ import com.intellij.openapi.command.CommandProcessor
|
|||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.LogicalPosition
|
|
||||||
import com.intellij.openapi.editor.actions.EnterAction
|
import com.intellij.openapi.editor.actions.EnterAction
|
||||||
import com.intellij.openapi.editor.event.EditorMouseEvent
|
import com.intellij.openapi.editor.event.EditorMouseEvent
|
||||||
import com.intellij.openapi.editor.event.EditorMouseListener
|
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.UserDataHolder
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
|
||||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
import com.intellij.psi.util.PsiUtilBase
|
import com.intellij.psi.util.PsiUtilBase
|
||||||
import com.intellij.util.containers.ContainerUtil
|
|
||||||
import com.maddyhome.idea.vim.EventFacade
|
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.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimCaret
|
import com.maddyhome.idea.vim.api.VimCaret
|
||||||
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
import com.maddyhome.idea.vim.api.VimChangeGroupBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
|
||||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
|
||||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
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.getLineStartForOffset
|
||||||
import com.maddyhome.idea.vim.api.getText
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.lineLength
|
|
||||||
import com.maddyhome.idea.vim.api.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.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.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.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.endOffsetInclusive
|
|
||||||
import com.maddyhome.idea.vim.helper.findNumberUnderCursor
|
|
||||||
import com.maddyhome.idea.vim.helper.findNumbersInRange
|
|
||||||
import com.maddyhome.idea.vim.helper.inInsertMode
|
import com.maddyhome.idea.vim.helper.inInsertMode
|
||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
|
||||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
|
||||||
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
|
import com.maddyhome.idea.vim.key.KeyHandlerKeeper.Companion.getInstance
|
||||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
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.IjVimCaret
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.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
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
|
||||||
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
|
* Provides all the insert/replace related functionality
|
||||||
*/
|
*/
|
||||||
class ChangeGroup : VimChangeGroupBase() {
|
class ChangeGroup : VimChangeGroupBase() {
|
||||||
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
|
|
||||||
private val listener: EditorMouseListener = object : EditorMouseListener {
|
private val listener: EditorMouseListener = object : EditorMouseListener {
|
||||||
override fun mouseClicked(event: EditorMouseEvent) {
|
override fun mouseClicked(event: EditorMouseEvent) {
|
||||||
val editor = event.editor
|
val editor = event.editor
|
||||||
@@ -103,9 +64,15 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
val ijContext = context.ij
|
val ijContext = context.ij
|
||||||
val doc = vimEditor.editor.document
|
val doc = vimEditor.editor.document
|
||||||
|
|
||||||
val undo = injector.undo
|
val undo = injector.undo
|
||||||
|
when (undo) {
|
||||||
|
is VimKeyBasedUndoService -> undo.setInsertNonMergeUndoKey()
|
||||||
|
is VimTimestampBasedUndoService -> {
|
||||||
val nanoTime = System.nanoTime()
|
val nanoTime = System.nanoTime()
|
||||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
||||||
|
}
|
||||||
|
}
|
||||||
CommandProcessor.getInstance().executeCommand(
|
CommandProcessor.getInstance().executeCommand(
|
||||||
editor.project, {
|
editor.project, {
|
||||||
ApplicationManager.getApplication()
|
ApplicationManager.getApplication()
|
||||||
@@ -145,161 +112,9 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDeleteRangeAndType2(
|
override fun processBackspace(editor: VimEditor, context: ExecutionContext) {
|
||||||
editor: VimEditor,
|
injector.actionExecutor.executeAction(editor, name = IdeActions.ACTION_EDITOR_BACKSPACE, context = context)
|
||||||
caret: VimCaret,
|
injector.scroll.scrollCaretIntoView(editor)
|
||||||
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) {
|
private fun restoreCursor(editor: VimEditor, caret: VimCaret, startLine: Int) {
|
||||||
@@ -310,88 +125,13 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun reformatCode(editor: VimEditor, start: Int, end: Int) {
|
||||||
* 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) {
|
|
||||||
val project = (editor as IjVimEditor).editor.project ?: return
|
val project = (editor as IjVimEditor).editor.project ?: return
|
||||||
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
val file = PsiUtilBase.getPsiFileInEditor(editor.editor, project) ?: return
|
||||||
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
val textRange = com.intellij.openapi.util.TextRange.create(start, end)
|
||||||
CodeStyleManager.getInstance(project).reformatText(file, listOf(textRange))
|
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(
|
override fun autoIndentRange(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
caret: VimCaret,
|
caret: VimCaret,
|
||||||
@@ -405,10 +145,10 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
|
|
||||||
// FIXME: Here we do selection, and it is not a good idea, because it updates primary selection in Linux
|
// FIXME: Here we do selection, and it is not a good idea, because it updates primary selection in Linux
|
||||||
// FIXME: I'll leave here a dirty fix that restores primary selection, but it would be better to rewrite this method
|
// FIXME: I'll leave here a dirty fix that restores primary selection, but it would be better to rewrite this method
|
||||||
var primaryTextAndTransferableData: Pair<String, List<Any>?>? = null
|
var copiedText: IjVimCopiedText? = null
|
||||||
try {
|
try {
|
||||||
if (injector.registerGroup.isPrimaryRegisterSupported()) {
|
if (injector.registerGroup.isPrimaryRegisterSupported()) {
|
||||||
primaryTextAndTransferableData = injector.clipboardManager.getPrimaryTextAndTransferableData()
|
copiedText = injector.clipboardManager.getPrimaryContent(editor, context) as IjVimCopiedText
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
|
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
|
||||||
@@ -434,373 +174,22 @@ class ChangeGroup : VimChangeGroupBase() {
|
|||||||
afterAction.invoke()
|
afterAction.invoke()
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (primaryTextAndTransferableData != null) {
|
if (copiedText != null) {
|
||||||
injector.clipboardManager.setPrimaryText(
|
injector.clipboardManager.setPrimaryContent(editor, context, copiedText)
|
||||||
primaryTextAndTransferableData.first,
|
|
||||||
primaryTextAndTransferableData.first,
|
|
||||||
primaryTextAndTransferableData.second ?: emptyList()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
|
// FIXME: [isPrimaryRegisterSupported()] is not implemented perfectly, so there might be thrown an exception after trying to access the primary selection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun indentLines(
|
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.add", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = 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 = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addInsertListener(listener: VimInsertListener) {
|
fun addInsertListener(listener: VimInsertListener) {
|
||||||
insertListeners.add(listener)
|
injector.listenersNotifier.modeChangeListeners.add(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(message = "Please use listenersNotifier", replaceWith = ReplaceWith("injector.listenersNotifier.modeChangeListeners.remove", imports = ["import com.maddyhome.idea.vim.api.injector"]))
|
||||||
fun removeInsertListener(listener: VimInsertListener) {
|
fun removeInsertListener(listener: VimInsertListener) {
|
||||||
insertListeners.remove(listener)
|
injector.listenersNotifier.modeChangeListeners.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
@@ -1,78 +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.diagnostic.Logger;
|
|
||||||
import com.maddyhome.idea.vim.api.VimDigraphGroupBase;
|
|
||||||
import com.maddyhome.idea.vim.api.VimEditor;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class DigraphGroup extends VimDigraphGroupBase {
|
|
||||||
|
|
||||||
public void showDigraphs(@NotNull VimEditor editor) {
|
|
||||||
int width = EditorHelper.getApproximateScreenWidth(((IjVimEditor) editor).getEditor());
|
|
||||||
if (width < 10) {
|
|
||||||
width = 80;
|
|
||||||
}
|
|
||||||
int colCount = width / 12;
|
|
||||||
int height = (int)Math.ceil((double) getDigraphs().size() / (double)colCount);
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("width=" + width);
|
|
||||||
logger.debug("colCount=" + colCount);
|
|
||||||
logger.debug("height=" + height);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder res = new StringBuilder();
|
|
||||||
int cnt = 0;
|
|
||||||
for (Character code : getKeys().keySet()) {
|
|
||||||
String key = getKeys().get(code);
|
|
||||||
|
|
||||||
res.append(key);
|
|
||||||
res.append(' ');
|
|
||||||
if (code < 32) {
|
|
||||||
res.append('^');
|
|
||||||
res.append((char)(code + '@'));
|
|
||||||
}
|
|
||||||
else if (code >= 128 && code <= 159) {
|
|
||||||
res.append('~');
|
|
||||||
res.append((char)(code - 128 + '@'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.append(code);
|
|
||||||
res.append(' ');
|
|
||||||
}
|
|
||||||
res.append(' ');
|
|
||||||
if (code < 0x1000) {
|
|
||||||
res.append('0');
|
|
||||||
}
|
|
||||||
if (code < 0x100) {
|
|
||||||
res.append('0');
|
|
||||||
}
|
|
||||||
if (code < 0x10) {
|
|
||||||
res.append('0');
|
|
||||||
}
|
|
||||||
res.append(Integer.toHexString(code));
|
|
||||||
res.append(" ");
|
|
||||||
|
|
||||||
cnt++;
|
|
||||||
if (cnt == colCount) {
|
|
||||||
res.append('\n');
|
|
||||||
cnt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExOutputModel.getInstance(((IjVimEditor) editor).getEditor()).output(res.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
|
|
||||||
}
|
|
@@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.group;
|
package com.maddyhome.idea.vim.group;
|
||||||
|
|
||||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
|
||||||
import com.intellij.find.EditorSearchSession;
|
import com.intellij.find.EditorSearchSession;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
|
||||||
import com.intellij.openapi.client.ClientAppSession;
|
import com.intellij.openapi.client.ClientAppSession;
|
||||||
import com.intellij.openapi.client.ClientKind;
|
import com.intellij.openapi.client.ClientKind;
|
||||||
import com.intellij.openapi.client.ClientSessionsManager;
|
import com.intellij.openapi.client.ClientSessionsManager;
|
||||||
@@ -23,12 +21,10 @@ import com.intellij.openapi.editor.event.CaretListener;
|
|||||||
import com.intellij.openapi.editor.ex.EditorEx;
|
import com.intellij.openapi.editor.ex.EditorEx;
|
||||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.maddyhome.idea.vim.KeyHandler;
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.api.*;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||||
@@ -218,47 +214,10 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
|
if (injector.getApplication().isUnitTest()) {
|
||||||
// editors. 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 if they are 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, interactive stdin/stdout console output in run configurations 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.
|
|
||||||
// Finally, we have a special check for diff windows. If we compare against clipboard, we get a diff editor that is
|
|
||||||
// not file based, is writable, and not a viewer, but we don't want to treat this as an interactive editor.
|
|
||||||
// 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) &&
|
|
||||||
editor.getEditorKind() != EditorKind.DIFF) {
|
|
||||||
switchToInsertMode.run();
|
|
||||||
}
|
|
||||||
ApplicationManager.getApplication().invokeLater(
|
|
||||||
() -> {
|
|
||||||
if (editor.isDisposed()) return;
|
|
||||||
ConsoleViewImpl consoleView = editor.getUserData(ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW);
|
|
||||||
if (consoleView != null && consoleView.isRunning() && !CommandStateHelper.inInsertMode(editor)) {
|
|
||||||
switchToInsertMode.run();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void editorDeinit(@NotNull Editor editor) {
|
public void editorDeinit(@NotNull Editor editor) {
|
||||||
deinitLineNumbers(editor);
|
deinitLineNumbers(editor);
|
||||||
@@ -279,9 +238,8 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
|
VimPlugin.getNotifications(project).notifyAboutIdeaJoin(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public Element getState() {
|
public @Nullable Element getState() {
|
||||||
Element element = new Element("editor");
|
Element element = new Element("editor");
|
||||||
saveData(element);
|
saveData(element);
|
||||||
return element;
|
return element;
|
||||||
@@ -357,18 +315,16 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VimEditor> getEditors() {
|
public @NotNull Collection<VimEditor> getEditors() {
|
||||||
return getLocalEditors()
|
return getLocalEditors()
|
||||||
.filter(UserDataManager::getVimInitialised)
|
.filter(UserDataManager::getVimInitialised)
|
||||||
.map(IjVimEditor::new)
|
.map(IjVimEditor::new)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
public @NotNull Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||||
return getLocalEditors()
|
return getLocalEditors()
|
||||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||||
@@ -388,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
|
// events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
|
||||||
// the remote editors.
|
// the remote editors.
|
||||||
// This invocation will always get local editors, regardless of the current context.
|
// 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()) {
|
if (!appSessions.isEmpty()) {
|
||||||
ClientAppSession localSession = appSessions.get(0);
|
ClientAppSession localSession = appSessions.get(0);
|
||||||
return localSession.getService(ClientEditorManager.class).editors();
|
return localSession.getService(ClientEditorManager.class).editors();
|
||||||
@@ -416,11 +372,15 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
|||||||
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
|
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
|
||||||
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
|
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
|
||||||
if (activeCommandLine != null) {
|
if (activeCommandLine != null) {
|
||||||
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), false);
|
activeCommandLine.close(true, false);
|
||||||
}
|
}
|
||||||
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
|
VimOutputPanel outputPanel = injector.getOutputPanel().getCurrentOutputPanel();
|
||||||
if (exOutputModel != null) {
|
if (outputPanel != null) {
|
||||||
exOutputModel.close();
|
outputPanel.close();
|
||||||
|
}
|
||||||
|
VimModalInput modalInput = injector.getModalInput().getCurrentModalInput();
|
||||||
|
if (modalInput != null) {
|
||||||
|
modalInput.deactivate(true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -42,7 +42,6 @@ import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
|||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.execute
|
import com.maddyhome.idea.vim.newapi.execute
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -173,20 +172,20 @@ class FileGroup : VimFileBase() {
|
|||||||
/**
|
/**
|
||||||
* Saves specific file in the project.
|
* Saves specific file in the project.
|
||||||
*/
|
*/
|
||||||
override fun saveFile(context: ExecutionContext) {
|
override fun saveFile(editor: VimEditor, context: ExecutionContext) {
|
||||||
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
|
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
|
||||||
injector.nativeActionManager.saveAll
|
injector.nativeActionManager.saveAll
|
||||||
} else {
|
} else {
|
||||||
injector.nativeActionManager.saveCurrent
|
injector.nativeActionManager.saveCurrent
|
||||||
}
|
}
|
||||||
action.execute(context)
|
action.execute(editor, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves all files in the project.
|
* Saves all files in the project.
|
||||||
*/
|
*/
|
||||||
override fun saveFiles(context: ExecutionContext) {
|
override fun saveFiles(editor: VimEditor, context: ExecutionContext) {
|
||||||
injector.nativeActionManager.saveAll.execute(context)
|
injector.nativeActionManager.saveAll.execute(editor, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,7 +270,7 @@ class FileGroup : VimFileBase() {
|
|||||||
val msg = StringBuilder()
|
val msg = StringBuilder()
|
||||||
val doc = editor.document
|
val doc = editor.document
|
||||||
|
|
||||||
if (getInstance(IjVimEditor(editor)).mode !is VISUAL) {
|
if (injector.vimState.mode !is VISUAL) {
|
||||||
val lp = editor.caretModel.logicalPosition
|
val lp = editor.caretModel.logicalPosition
|
||||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
||||||
var endoff = doc.getLineEndOffset(lp.line)
|
var endoff = doc.getLineEndOffset(lp.line)
|
||||||
|
@@ -14,9 +14,7 @@ import com.intellij.openapi.components.State;
|
|||||||
import com.intellij.openapi.components.Storage;
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.history.HistoryBlock;
|
import com.maddyhome.idea.vim.history.*;
|
||||||
import com.maddyhome.idea.vim.history.HistoryEntry;
|
|
||||||
import com.maddyhome.idea.vim.history.VimHistoryBase;
|
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -35,21 +33,20 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
|||||||
logger.debug("saveData");
|
logger.debug("saveData");
|
||||||
Element hist = new Element("history");
|
Element hist = new Element("history");
|
||||||
|
|
||||||
saveData(hist, SEARCH);
|
for (Type type : getHistories().keySet()) {
|
||||||
saveData(hist, COMMAND);
|
saveData(hist, type);
|
||||||
saveData(hist, EXPRESSION);
|
}
|
||||||
saveData(hist, INPUT);
|
|
||||||
|
|
||||||
element.addContent(hist);
|
element.addContent(hist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveData(@NotNull Element element, String key) {
|
private void saveData(@NotNull Element element, VimHistory.Type type) {
|
||||||
final HistoryBlock block = getHistories().get(key);
|
final HistoryBlock block = getHistories().get(type);
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Element root = new Element("history-" + key);
|
final Element root = new Element("history-" + typeToKey(type));
|
||||||
|
|
||||||
for (HistoryEntry entry : block.getEntries()) {
|
for (HistoryEntry entry : block.getEntries()) {
|
||||||
final Element entryElement = new Element("entry");
|
final Element entryElement = new Element("entry");
|
||||||
@@ -67,10 +64,10 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
readData(hist, SEARCH);
|
for (Element child : hist.getChildren()) {
|
||||||
readData(hist, COMMAND);
|
String key = child.getName().replace("history-", "");
|
||||||
readData(hist, EXPRESSION);
|
readData(hist, key);
|
||||||
readData(hist, INPUT);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readData(@NotNull Element element, String key) {
|
private void readData(@NotNull Element element, String key) {
|
||||||
@@ -80,7 +77,7 @@ public class HistoryGroup extends VimHistoryBase implements PersistentStateCompo
|
|||||||
}
|
}
|
||||||
|
|
||||||
block = new HistoryBlock();
|
block = new HistoryBlock();
|
||||||
getHistories().put(key, block);
|
getHistories().put(getTypeForString(key), block);
|
||||||
|
|
||||||
final Element root = element.getChild("history-" + key);
|
final Element root = element.getChild("history-" + key);
|
||||||
if (root != null) {
|
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
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Element getState() {
|
public Element getState() {
|
||||||
|
@@ -26,7 +26,7 @@ class IjVimPsiService: VimPsiService {
|
|||||||
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
||||||
val psiElement = psiFile.findElementAt(pos) ?: return null
|
val psiElement = psiFile.findElementAt(pos) ?: return null
|
||||||
val language = psiElement.language
|
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 psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
|
||||||
val commentText = psiComment.text
|
val commentText = psiComment.text
|
||||||
|
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.group;
|
package com.maddyhome.idea.vim.group;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||||
import com.intellij.openapi.actionSystem.*;
|
import com.intellij.openapi.actionSystem.*;
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
||||||
@@ -17,21 +16,16 @@ import com.intellij.openapi.application.ApplicationManager;
|
|||||||
import com.intellij.openapi.components.PersistentStateComponent;
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
import com.intellij.openapi.components.State;
|
import com.intellij.openapi.components.State;
|
||||||
import com.intellij.openapi.components.Storage;
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
|
||||||
import com.intellij.openapi.keymap.Keymap;
|
import com.intellij.openapi.keymap.Keymap;
|
||||||
import com.intellij.openapi.keymap.KeymapManager;
|
import com.intellij.openapi.keymap.KeymapManager;
|
||||||
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
|
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
|
||||||
|
import com.intellij.util.containers.MultiMap;
|
||||||
import com.maddyhome.idea.vim.EventFacade;
|
import com.maddyhome.idea.vim.EventFacade;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
|
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
|
||||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
|
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
|
||||||
import com.maddyhome.idea.vim.api.NativeAction;
|
import com.maddyhome.idea.vim.api.*;
|
||||||
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.command.MappingMode;
|
import com.maddyhome.idea.vim.command.MappingMode;
|
||||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
|
||||||
import com.maddyhome.idea.vim.key.*;
|
import com.maddyhome.idea.vim.key.*;
|
||||||
import com.maddyhome.idea.vim.newapi.IjNativeAction;
|
import com.maddyhome.idea.vim.newapi.IjNativeAction;
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||||
@@ -61,8 +55,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
private static final @NonNls String OWNER_ATTRIBUTE = "owner";
|
private static final @NonNls String OWNER_ATTRIBUTE = "owner";
|
||||||
private static final String TEXT_ELEMENT = "text";
|
private static final String TEXT_ELEMENT = "text";
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(KeyGroup.class);
|
|
||||||
|
|
||||||
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
|
public void registerRequiredShortcutKeys(@NotNull VimEditor editor) {
|
||||||
EventFacade.getInstance()
|
EventFacade.getInstance()
|
||||||
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()),
|
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(getRequiredShortcutKeys()),
|
||||||
@@ -80,25 +72,6 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
((IjVimEditor)editor).getEditor().getComponent());
|
((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
|
@Override
|
||||||
public void updateShortcutKeysRegistration() {
|
public void updateShortcutKeysRegistration() {
|
||||||
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
||||||
@@ -221,8 +194,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
|
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
|
||||||
|
|
||||||
for (MappingMode mappingMode : command.getModes()) {
|
for (MappingMode mappingMode : command.getModes()) {
|
||||||
Node<LazyVimCommand> node = getKeyRoot(mappingMode);
|
getBuiltinCommandsTrie(mappingMode).add(keyStrokes, command);
|
||||||
NodesKt.addLeafs(node, keyStrokes, command);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,53 +219,79 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
return new CustomShortcutSet(shortcuts.toArray(new Shortcut[0]));
|
return new CustomShortcutSet(shortcuts.toArray(new Shortcut[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull List<Pair<EnumSet<MappingMode>, MappingInfo>> getKeyMappingRows(@NotNull Set<? extends MappingMode> modes) {
|
private static @NotNull List<Pair<Set<MappingMode>, MappingInfo>> getKeyMappingRows(@NotNull Set<? extends MappingMode> modes,
|
||||||
final Map<ImmutableList<KeyStroke>, EnumSet<MappingMode>> actualModes = new HashMap<>();
|
@NotNull List<? extends KeyStroke> prefix) {
|
||||||
|
// Some map commands set a mapping for more than one mode (e.g. `map` sets for Normal, Visual, Select and
|
||||||
|
// Op-pending). Vim treats this as a single mapping, and when listing all maps only lists it once, with the
|
||||||
|
// appropriate mode indicator(s) in the first column (NVO is a space char). If the lhs mapping is changed or cleared
|
||||||
|
// for one of the modes, the original mapping is still a single map for the remaining modes, and the indicator
|
||||||
|
// changes. E.g. `map foo bar` followed by `sunmap foo` would result in `nox foo bar` in the output to `map`.
|
||||||
|
// Vim doesn't do automatic grouping - `nmap foo bar` followed by `omap foo bar` and `vmap foo bar` would result in
|
||||||
|
// 3 lines in the output to `map` - one for `n`, one for `o` and one for `v`.
|
||||||
|
// We store mappings separately per mode (to simplify lookup, especially when matching prefixes), but want to have
|
||||||
|
// the same behaviour as Vim in map output. So we store the original modes with the mapping and check they're still
|
||||||
|
// valid as we collect output
|
||||||
|
final List<Pair<Set<MappingMode>, MappingInfo>> rows = new ArrayList<>();
|
||||||
|
final MultiMap<List<? extends KeyStroke>, Set<MappingMode>> multiModeMappings = MultiMap.create();
|
||||||
|
final List<KeyStroke> fromKeys = new ArrayList<>();
|
||||||
|
|
||||||
for (MappingMode mode : modes) {
|
for (MappingMode mode : modes) {
|
||||||
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
||||||
for (List<? extends KeyStroke> fromKeys : mapping) {
|
|
||||||
final ImmutableList<KeyStroke> key = ImmutableList.copyOf(fromKeys);
|
final Iterator<KeyMappingEntry> iterator = mapping.getAll(prefix).iterator();
|
||||||
final EnumSet<MappingMode> value = actualModes.get(key);
|
while (iterator.hasNext()) {
|
||||||
final EnumSet<MappingMode> newValue;
|
final KeyMappingEntry entry = iterator.next();
|
||||||
if (value != null) {
|
final MappingInfo mappingInfo = entry.getMappingInfo();
|
||||||
newValue = value.clone();
|
|
||||||
newValue.add(mode);
|
final Set<@NotNull MappingMode> originalModes = mappingInfo.getOriginalModes();
|
||||||
|
if (originalModes.size() == 1) {
|
||||||
|
rows.add(new Pair<>(originalModes, mappingInfo));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newValue = EnumSet.of(mode);
|
entry.collectPath(fromKeys);
|
||||||
}
|
if (!multiModeMappings.get(fromKeys).contains(originalModes)) {
|
||||||
actualModes.put(key, newValue);
|
multiModeMappings.putValue(new ArrayList<>(fromKeys), originalModes);
|
||||||
}
|
rows.add(new Pair<>(getModesForMapping(fromKeys, originalModes), mappingInfo));
|
||||||
}
|
|
||||||
final List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = new ArrayList<>();
|
|
||||||
for (Map.Entry<ImmutableList<KeyStroke>, EnumSet<MappingMode>> entry : actualModes.entrySet()) {
|
|
||||||
final ArrayList<KeyStroke> fromKeys = new ArrayList<>(entry.getKey());
|
|
||||||
final EnumSet<MappingMode> mappingModes = entry.getValue();
|
|
||||||
if (!mappingModes.isEmpty()) {
|
|
||||||
final MappingMode mode = mappingModes.iterator().next();
|
|
||||||
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
|
||||||
final MappingInfo mappingInfo = mapping.get(fromKeys);
|
|
||||||
if (mappingInfo != null) {
|
|
||||||
rows.add(new Pair<>(mappingModes, mappingInfo));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.sort(Comparator.comparing(Pair<EnumSet<MappingMode>, MappingInfo>::getSecond));
|
}
|
||||||
|
rows.sort(Comparator.comparing(Pair<Set<MappingMode>, MappingInfo>::getSecond));
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @NotNull Set<MappingMode> getModesForMapping(@NotNull List<? extends KeyStroke> keyStrokes,
|
||||||
|
@NotNull Set<MappingMode> originalMappingModes) {
|
||||||
|
final Set<MappingMode> actualModes = EnumSet.noneOf(MappingMode.class);
|
||||||
|
for (MappingMode mode : originalMappingModes) {
|
||||||
|
final MappingInfo mappingInfo = VimPlugin.getKey().getKeyMapping(mode).get(keyStrokes);
|
||||||
|
if (mappingInfo != null && mappingInfo.getOriginalModes() == originalMappingModes) {
|
||||||
|
actualModes.add(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actualModes;
|
||||||
|
}
|
||||||
|
|
||||||
private static @NotNull @NonNls String getModesStringCode(@NotNull Set<MappingMode> modes) {
|
private static @NotNull @NonNls String getModesStringCode(@NotNull Set<MappingMode> modes) {
|
||||||
if (modes.equals(MappingMode.NVO)) {
|
if (modes.equals(MappingMode.IC)) return "!";
|
||||||
return "";
|
if (modes.equals(MappingMode.NVO)) return " ";
|
||||||
|
if (modes.equals(MappingMode.C)) return "c";
|
||||||
|
if (modes.equals(MappingMode.I)) return "i";
|
||||||
|
//if (modes.equals(MappingMode.L)) return "l";
|
||||||
|
|
||||||
|
// The following modes are concatenated
|
||||||
|
String mode = "";
|
||||||
|
if (modes.containsAll(MappingMode.N)) mode += "n";
|
||||||
|
if (modes.containsAll(MappingMode.O)) mode += "o";
|
||||||
|
|
||||||
|
if (modes.containsAll(MappingMode.V)) {
|
||||||
|
mode += "v";
|
||||||
}
|
}
|
||||||
else if (modes.contains(MappingMode.INSERT)) {
|
else {
|
||||||
return "i";
|
if (modes.containsAll(MappingMode.X)) mode += "x";
|
||||||
|
if (modes.containsAll(MappingMode.S)) mode += "s";
|
||||||
}
|
}
|
||||||
else if (modes.contains(MappingMode.NORMAL)) {
|
return mode;
|
||||||
return "n";
|
|
||||||
}
|
|
||||||
// TODO: Add more codes
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NotNull List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
private @NotNull List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||||
@@ -357,7 +355,23 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull VimEditor editor) {
|
public boolean showKeyMappings(@NotNull Set<? extends MappingMode> modes, @NotNull List<? extends KeyStroke> prefix, @NotNull VimEditor editor) {
|
||||||
return showKeyMappings(modes, ((IjVimEditor) editor).getEditor());
|
List<Pair<Set<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes, prefix);
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
for (Pair<Set<MappingMode>, MappingInfo> row : rows) {
|
||||||
|
MappingInfo mappingInfo = row.getSecond();
|
||||||
|
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 3, ' '));
|
||||||
|
builder.append(StringsKt.padEnd(VimInjectorKt.getInjector().getParser().toKeyNotation(mappingInfo.getFromKeys()) + " ", 12, ' '));
|
||||||
|
builder.append(mappingInfo.isRecursive() ? " " : "*"); // Or `&` if script-local mappings being recursive
|
||||||
|
builder.append(" "); // Should be `@` if it's a buffer-local mapping
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,7 @@ import com.maddyhome.idea.vim.command.Argument
|
|||||||
import com.maddyhome.idea.vim.command.MotionType
|
import com.maddyhome.idea.vim.command.MotionType
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
import com.maddyhome.idea.vim.handler.ExternalActionHandler
|
||||||
import com.maddyhome.idea.vim.handler.Motion
|
import com.maddyhome.idea.vim.handler.Motion
|
||||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||||
@@ -47,12 +47,14 @@ import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
|||||||
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
||||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
|
||||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
|
import com.maddyhome.idea.vim.state.mode.ReturnTo
|
||||||
|
import com.maddyhome.idea.vim.state.mode.returnTo
|
||||||
import org.jetbrains.annotations.Range
|
import org.jetbrains.annotations.Range
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -191,21 +193,16 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
argument: Argument,
|
argument: Argument,
|
||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): TextRange? {
|
): TextRange? {
|
||||||
|
if (argument !is Argument.Motion) {
|
||||||
|
throw RuntimeException("Unexpected argument passed to getMotionRange2: $argument")
|
||||||
|
}
|
||||||
|
|
||||||
var start: Int
|
var start: Int
|
||||||
var end: Int
|
var end: Int
|
||||||
if (argument.type === Argument.Type.OFFSETS) {
|
|
||||||
val offsets = argument.offsets[caret.vim] ?: return null
|
|
||||||
val (first, second) = offsets.getNativeStartAndEnd()
|
|
||||||
start = first
|
|
||||||
end = second
|
|
||||||
} else {
|
|
||||||
val cmd = argument.motion
|
|
||||||
// Normalize the counts between the command and the motion argument
|
|
||||||
val cnt = cmd.count * operatorArguments.count1
|
|
||||||
val raw = if (operatorArguments.count0 == 0 && cmd.rawCount == 0) 0 else cnt
|
|
||||||
if (cmd.action is MotionActionHandler) {
|
|
||||||
val action = cmd.action as MotionActionHandler
|
|
||||||
|
|
||||||
|
val action = argument.motion
|
||||||
|
when (action) {
|
||||||
|
is MotionActionHandler -> {
|
||||||
// This is where we are now
|
// This is where we are now
|
||||||
start = caret.offset
|
start = caret.offset
|
||||||
|
|
||||||
@@ -214,8 +211,8 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
editor.vim,
|
editor.vim,
|
||||||
caret.vim,
|
caret.vim,
|
||||||
IjEditorExecutionContext(context!!),
|
IjEditorExecutionContext(context!!),
|
||||||
cmd.argument,
|
argument.argument,
|
||||||
operatorArguments.withCount0(raw),
|
operatorArguments
|
||||||
)
|
)
|
||||||
|
|
||||||
// Invalid motion
|
// Invalid motion
|
||||||
@@ -231,22 +228,32 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
end++
|
end++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cmd.action is TextObjectActionHandler) {
|
}
|
||||||
val action = cmd.action as TextObjectActionHandler
|
|
||||||
val range =
|
is TextObjectActionHandler -> {
|
||||||
action.getRange(editor.vim, caret.vim, IjEditorExecutionContext(context!!), cnt, raw) ?: return null
|
val range = action.getRange(
|
||||||
|
editor.vim,
|
||||||
|
caret.vim,
|
||||||
|
IjEditorExecutionContext(context!!),
|
||||||
|
operatorArguments.count1,
|
||||||
|
operatorArguments.count0
|
||||||
|
) ?: return null
|
||||||
start = range.startOffset
|
start = range.startOffset
|
||||||
end = range.endOffset
|
end = range.endOffset
|
||||||
if (cmd.isLinewiseMotion()) end--
|
if (argument.isLinewiseMotion()) end--
|
||||||
} else {
|
|
||||||
throw RuntimeException(
|
|
||||||
"Commands doesn't take " + cmd.action.javaClass.simpleName + " as an operator",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ExternalActionHandler -> {
|
||||||
|
val range = action.getRange(caret.vim) ?: return null
|
||||||
|
start = range.startOffset
|
||||||
|
end = range.endOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw RuntimeException("Commands doesn't take " + action.javaClass.simpleName + " as an operator")
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is operated when it shouldn't be.
|
// This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is operated when it shouldn't be.
|
||||||
val id = argument.motion.action.id
|
val id = argument.motion.id
|
||||||
if (id == VimChangeGroupBase.VIM_MOTION_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_BIG_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_CAMEL_RIGHT) {
|
if (id == VimChangeGroupBase.VIM_MOTION_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_BIG_WORD_RIGHT || id == VimChangeGroupBase.VIM_MOTION_CAMEL_RIGHT) {
|
||||||
val text = editor.document.charsSequence.subSequence(start, end).toString()
|
val text = editor.document.charsSequence.subSequence(start, end).toString()
|
||||||
val lastNewLine = text.lastIndexOf('\n')
|
val lastNewLine = text.lastIndexOf('\n')
|
||||||
@@ -256,6 +263,7 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TextRange(start, end)
|
return TextRange(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,19 +315,32 @@ internal class MotionGroup : VimMotionGroupBase() {
|
|||||||
val editor = fileEditor.editor
|
val editor = fileEditor.editor
|
||||||
if (!editor.isDisposed) {
|
if (!editor.isDisposed) {
|
||||||
editor.vim.let { vimEditor ->
|
editor.vim.let { vimEditor ->
|
||||||
when (vimEditor.vimStateMachine.mode) {
|
when (vimEditor.mode) {
|
||||||
is Mode.VISUAL -> {
|
is Mode.VISUAL -> {
|
||||||
vimEditor.exitVisualMode()
|
vimEditor.exitVisualMode()
|
||||||
KeyHandler.getInstance().reset(vimEditor)
|
KeyHandler.getInstance().reset(vimEditor)
|
||||||
}
|
}
|
||||||
is Mode.CMD_LINE -> {
|
is Mode.CMD_LINE -> {
|
||||||
injector.processGroup.cancelExEntry(vimEditor, false)
|
val commandLine = injector.commandLine.getActiveCommandLine() ?: return
|
||||||
ExOutputModel.tryGetInstance(editor)?.close()
|
commandLine.close(refocusOwningEditor = false, resetCaret = false)
|
||||||
|
injector.outputPanel.getCurrentOutputPanel()?.close()
|
||||||
}
|
}
|
||||||
else -> {}
|
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")
|
@Suppress("unused")
|
||||||
constructor() : this(null)
|
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() {
|
fun notifyAboutIdeaPut() {
|
||||||
val notification = Notification(
|
val notification = Notification(
|
||||||
IDEAVIM_NOTIFICATION_ID,
|
IDEAVIM_NOTIFICATION_ID,
|
||||||
@@ -182,8 +201,8 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
).notify(project)
|
).notify(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyActionId(id: String?) {
|
fun notifyActionId(id: String?, candidates: List<String>? = null) {
|
||||||
ActionIdNotifier.notifyActionId(id, project)
|
ActionIdNotifier.notifyActionId(id, project, candidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
|
fun notifyKeymapIssues(issues: ArrayList<KeyMapIssue>) {
|
||||||
@@ -259,20 +278,31 @@ internal class NotificationService(private val project: Project?) {
|
|||||||
|
|
||||||
object ActionIdNotifier {
|
object ActionIdNotifier {
|
||||||
private var notification: Notification? = null
|
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()
|
notification?.expire()
|
||||||
|
|
||||||
val content = if (id != null) "Action id: $id" else NO_ID
|
val possibleIDs = candidates?.distinct()?.sorted()
|
||||||
Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, content, NotificationType.INFORMATION).let {
|
val content = when {
|
||||||
notification = it
|
id != null -> "Action ID: <code>$id</code><br><br>"
|
||||||
it.whenExpired { notification = null }
|
possibleIDs.isNullOrEmpty() -> "<i>Cannot detect action ID</i><br><br>"
|
||||||
it.setContent(it.content + "<br><br><small>Use ${ActionCenter.getToolwindowName()} to see previous ids</small>")
|
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())
|
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)
|
it.notify(project)
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import com.intellij.application.options.CodeStyle
|
|||||||
import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction
|
import com.intellij.codeStyle.AbstractConvertLineSeparatorsAction
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.EditorKind
|
import com.intellij.openapi.editor.EditorKind
|
||||||
import com.intellij.openapi.editor.EditorSettings.LineNumerationType
|
import com.intellij.openapi.editor.EditorSettings.LineNumerationType
|
||||||
import com.intellij.openapi.editor.ScrollPositionCalculator
|
import com.intellij.openapi.editor.ScrollPositionCalculator
|
||||||
@@ -19,8 +20,6 @@ import com.intellij.openapi.editor.ex.EditorEx
|
|||||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
|
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
|
||||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces
|
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
|
||||||
import com.intellij.openapi.fileEditor.TextEditor
|
|
||||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil
|
import com.intellij.openapi.fileEditor.impl.LoadTextUtil
|
||||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl
|
import com.intellij.openapi.fileEditor.impl.text.TextEditorImpl
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
@@ -158,25 +157,24 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup, InternalOpt
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
fun editorReleased(editor: Editor) {
|
||||||
// Vim only has one window, and it's not possible to close it. This means that editing a new file will always
|
// Vim always has at least one window; it's not possible to close it. Editing a new file will open a new buffer in
|
||||||
// reuse an existing window (opening a new window will always open from an existing window). More importantly,
|
// the current window, or it's possible to split the current buffer into a new window, or open a new buffer in a
|
||||||
// this means that any newly edited file will always get up-to-date local-to-window options. A new window is based
|
// new window. This is important for us because when Vim opens a new window, the new window's local options are
|
||||||
// on the opening window (treated as split then edit, so copy local + per-window "global" window values, then
|
// copied from the current window.
|
||||||
// apply the per-window "global" values) and an edit reapplies the per-window "global" values.
|
// In detail: splitting the current window gets a complete copy of local and per-window global option values.
|
||||||
// If we close all windows, and open a new one, we can only use the per-window "global" values from the fallback
|
// Editing a new file will split the current window and then edit the new buffer in-place.
|
||||||
// window, but this is only initialised when we first read `~/.ideavimrc` during startup. Vim would use the values
|
// IntelliJ does not always have an open window. It would be weird to close the last editor tab, and then open
|
||||||
// from the current window, so to simulate this, we should update the fallback window with the values from the
|
// the next tab with different options - the user would expect the editor to look like the last one did.
|
||||||
// window that was selected at the time that the last window was closed.
|
// Therefore, we have a dummy "fallback" window that captures the options of the last closed editor. When opening
|
||||||
// Unfortunately, we can't reliably know if a closing editor is the selected editor. Instead, we rely on selection
|
// an editor and there are no currently open editors, we use the fallback window to initialise the new window.
|
||||||
// change events. If an editor is losing selection and there is no new selection, we can assume this means that
|
// This callback tracks when editors are closed, and if the last editor in a project is being closed, updates the
|
||||||
// the last editor has been closed, and use the closed editor to update the fallback window
|
// fallback window's options.
|
||||||
//
|
val project = editor.project ?: return
|
||||||
// XXX: event.oldEditor will must probably return a disposed editor. So, it should be treated with care
|
if (!injector.editorGroup.getEditorsRaw()
|
||||||
if (event.newEditor == null) {
|
.any { it.ij != editor && it.ij.project === project && it.ij.editorKind == EditorKind.MAIN_EDITOR }
|
||||||
(event.oldEditor as? TextEditor)?.editor?.let {
|
) {
|
||||||
(VimPlugin.getOptionGroup() as OptionGroup).updateFallbackWindow(injector.fallbackWindow, it.vim)
|
(VimPlugin.getOptionGroup() as OptionGroup).updateFallbackWindow(injector.fallbackWindow, editor.vim)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -95,8 +95,6 @@ class ProcessGroup : VimProcessGroupBase() {
|
|||||||
val progressIndicator = ProgressIndicatorProvider.getInstance().progressIndicator
|
val progressIndicator = ProgressIndicatorProvider.getInstance().progressIndicator
|
||||||
val output = handler.runProcessWithProgressIndicator(progressIndicator)
|
val output = handler.runProcessWithProgressIndicator(progressIndicator)
|
||||||
|
|
||||||
lastCommand = command
|
|
||||||
|
|
||||||
if (output.isCancelled) {
|
if (output.isCancelled) {
|
||||||
// TODO: Vim will use whatever text has already been written to stdout
|
// 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
|
// For whatever reason, we're not getting any here, so just throw an exception
|
||||||
|
@@ -14,6 +14,7 @@ import com.intellij.openapi.components.State;
|
|||||||
import com.intellij.openapi.components.Storage;
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
|
import com.maddyhome.idea.vim.newapi.IjVimInjectorKt;
|
||||||
import com.maddyhome.idea.vim.register.Register;
|
import com.maddyhome.idea.vim.register.Register;
|
||||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||||
@@ -35,6 +36,10 @@ import java.util.List;
|
|||||||
})
|
})
|
||||||
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
|
public class RegisterGroup extends VimRegisterGroupBase implements PersistentStateComponent<Element> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
IjVimInjectorKt.initInjector();
|
||||||
|
}
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
|
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
|
||||||
|
|
||||||
public RegisterGroup() {
|
public RegisterGroup() {
|
||||||
|
@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.mark.Jump
|
|||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.newapi.ij
|
import com.maddyhome.idea.vim.newapi.ij
|
||||||
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
import org.jdom.Element
|
import org.jdom.Element
|
||||||
|
|
||||||
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
|
@State(name = "VimJumpsSettings", storages = [Storage(value = "\$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)])
|
||||||
@@ -65,6 +66,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun loadState(state: Element) {
|
override fun loadState(state: Element) {
|
||||||
|
initInjector()
|
||||||
val projectElements = state.getChildren("project")
|
val projectElements = state.getChildren("project")
|
||||||
for (projectElement in projectElements) {
|
for (projectElement in projectElements) {
|
||||||
val jumps = mutableListOf<Jump>()
|
val jumps = mutableListOf<Jump>()
|
||||||
@@ -89,6 +91,7 @@ internal class VimJumpServiceImpl : VimJumpServiceBase(), PersistentStateCompone
|
|||||||
|
|
||||||
internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||||
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
|
override fun recentPlaceAdded(changePlace: PlaceInfo, isChanged: Boolean) {
|
||||||
|
initInjector()
|
||||||
if (!injector.globalIjOptions().unifyjumps) return
|
if (!injector.globalIjOptions().unifyjumps) return
|
||||||
|
|
||||||
val jumpService = injector.jumpService
|
val jumpService = injector.jumpService
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.group
|
package com.maddyhome.idea.vim.group
|
||||||
|
|
||||||
import com.intellij.codeWithMe.ClientId
|
|
||||||
import com.intellij.ide.bookmark.Bookmark
|
import com.intellij.ide.bookmark.Bookmark
|
||||||
import com.intellij.ide.bookmark.BookmarkGroup
|
import com.intellij.ide.bookmark.BookmarkGroup
|
||||||
import com.intellij.ide.bookmark.BookmarksListener
|
import com.intellij.ide.bookmark.BookmarksListener
|
||||||
|
@@ -46,10 +46,13 @@ import com.maddyhome.idea.vim.put.PutData
|
|||||||
import com.maddyhome.idea.vim.put.VimPasteProvider
|
import com.maddyhome.idea.vim.put.VimPasteProvider
|
||||||
import com.maddyhome.idea.vim.put.VimPutBase
|
import com.maddyhome.idea.vim.put.VimPutBase
|
||||||
import com.maddyhome.idea.vim.register.RegisterConstants
|
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.SelectionType
|
||||||
import com.maddyhome.idea.vim.state.mode.isBlock
|
import com.maddyhome.idea.vim.state.mode.isBlock
|
||||||
import com.maddyhome.idea.vim.state.mode.isChar
|
import com.maddyhome.idea.vim.state.mode.isChar
|
||||||
import com.maddyhome.idea.vim.state.mode.isLine
|
import com.maddyhome.idea.vim.state.mode.isLine
|
||||||
|
import com.maddyhome.idea.vim.undo.VimKeyBasedUndoService
|
||||||
|
import com.maddyhome.idea.vim.undo.VimTimestampBasedUndoService
|
||||||
import java.awt.datatransfer.DataFlavor
|
import java.awt.datatransfer.DataFlavor
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -83,11 +86,17 @@ internal class PutGroup : VimPutBase() {
|
|||||||
val editor = (vimEditor as IjVimEditor).editor
|
val editor = (vimEditor as IjVimEditor).editor
|
||||||
val context = vimContext.context as DataContext
|
val context = vimContext.context as DataContext
|
||||||
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
val carets: MutableMap<Caret, RangeMarker> = mutableMapOf()
|
||||||
if (editor.isInsertMode) {
|
if (injector.vimState.mode is Mode.INSERT) {
|
||||||
val undo = injector.undo
|
|
||||||
val nanoTime = System.nanoTime()
|
val nanoTime = System.nanoTime()
|
||||||
|
|
||||||
|
val undo = injector.undo
|
||||||
|
when (undo) {
|
||||||
|
is VimKeyBasedUndoService -> undo.setInsertNonMergeUndoKey()
|
||||||
|
is VimTimestampBasedUndoService -> {
|
||||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
|
EditorHelper.getOrderedCaretsList(editor).forEach { caret ->
|
||||||
val startOffset =
|
val startOffset =
|
||||||
prepareDocumentAndGetStartOffsets(
|
prepareDocumentAndGetStartOffsets(
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.group.visual
|
package com.maddyhome.idea.vim.group.visual
|
||||||
|
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.diagnostic.trace
|
import com.intellij.openapi.diagnostic.trace
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
@@ -15,6 +16,8 @@ import com.maddyhome.idea.vim.KeyHandler
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.api.options
|
import com.maddyhome.idea.vim.api.options
|
||||||
|
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.controlNonVimSelectionChange
|
||||||
|
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl.predictMode
|
||||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
import com.maddyhome.idea.vim.helper.hasVisualSelection
|
||||||
@@ -63,12 +66,15 @@ internal object IdeaSelectionControl {
|
|||||||
// - There was no selection and now it is
|
// - There was no selection and now it is
|
||||||
// - There was a selection and now it doesn't exist
|
// - There was a selection and now it doesn't exist
|
||||||
// - There was a selection and now it exists as well (transforming char selection to line selection, for example)
|
// - There was a selection and now it exists as well (transforming char selection to line selection, for example)
|
||||||
if (initialMode?.hasVisualSelection == false && !editor.selectionModel.hasSelection(true)) {
|
val hasSelection = ApplicationManager.getApplication().runReadAction<Boolean> {
|
||||||
|
editor.selectionModel.hasSelection(true)
|
||||||
|
}
|
||||||
|
if (initialMode?.hasVisualSelection == false && !hasSelection) {
|
||||||
logger.trace { "Exiting without selection adjusting" }
|
logger.trace { "Exiting without selection adjusting" }
|
||||||
return@singleTask
|
return@singleTask
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editor.selectionModel.hasSelection(true)) {
|
if (hasSelection) {
|
||||||
if (editor.vim.inCommandLineMode && editor.vim.mode.returnTo().hasVisualSelection) {
|
if (editor.vim.inCommandLineMode && editor.vim.mode.returnTo().hasVisualSelection) {
|
||||||
logger.trace { "Modifying selection while in Command-line mode, most likely incsearch" }
|
logger.trace { "Modifying selection while in Command-line mode, most likely incsearch" }
|
||||||
return@singleTask
|
return@singleTask
|
||||||
|
@@ -9,8 +9,6 @@
|
|||||||
package com.maddyhome.idea.vim.group.visual
|
package com.maddyhome.idea.vim.group.visual
|
||||||
|
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.mode
|
|
||||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer.singleTask
|
|
||||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import java.awt.event.ActionEvent
|
import java.awt.event.ActionEvent
|
||||||
|
@@ -16,6 +16,7 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.startup.ProjectActivity
|
import com.intellij.openapi.startup.ProjectActivity
|
||||||
import com.maddyhome.idea.vim.api.key
|
import com.maddyhome.idea.vim.api.key
|
||||||
|
import com.maddyhome.idea.vim.newapi.initInjector
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the chain of handlers for esc and enter
|
* 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")
|
private val editorHandlers = ExtensionPointName<EditorActionHandlerBean>("com.intellij.editorActionHandler")
|
||||||
|
|
||||||
override suspend fun execute(project: Project) {
|
override suspend fun execute(project: Project) {
|
||||||
|
initInjector()
|
||||||
|
|
||||||
if (!enableOctopus) return
|
if (!enableOctopus) return
|
||||||
|
|
||||||
val escHandlers = editorHandlers.extensionList
|
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() {
|
internal abstract class IdeActionHandler(private val actionName: String) : VimActionHandler.SingleExecution() {
|
||||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||||
injector.actionExecutor.executeAction(actionName, context)
|
injector.actionExecutor.executeAction(editor, name = actionName, context = context)
|
||||||
injector.scroll.scrollCaretIntoView(editor)
|
injector.scroll.scrollCaretIntoView(editor)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -183,6 +183,7 @@ internal abstract class OctopusHandler(private val nextHandler: EditorActionHand
|
|||||||
* - App code - set handler after
|
* - App code - set handler after
|
||||||
* - Template - doesn't intersect with enter anymore
|
* - Template - doesn't intersect with enter anymore
|
||||||
* - rd.client.editor.enter - set handler before. Otherwise, rider will add new line on enter even in normal mode
|
* - rd.client.editor.enter - set handler before. Otherwise, rider will add new line on enter even in normal mode
|
||||||
|
* - inline.completion.enter - set handler before. Otherwise, AI completion is not invoked on enter.
|
||||||
*
|
*
|
||||||
* This rule is disabled due to VIM-3124
|
* This rule is disabled due to VIM-3124
|
||||||
* - before terminalEnter - not necessary, but terminalEnter causes "file is read-only" tooltip for readonly files VIM-3122
|
* - before terminalEnter - not necessary, but terminalEnter causes "file is read-only" tooltip for readonly files VIM-3122
|
||||||
@@ -218,13 +219,17 @@ internal class VimEnterHandler(nextHandler: EditorActionHandler?) : VimKeyHandle
|
|||||||
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
|
internal class VimEscHandler(nextHandler: EditorActionHandler) : VimKeyHandler(nextHandler) {
|
||||||
override val key: String = "<Esc>"
|
override val key: String = "<Esc>"
|
||||||
|
|
||||||
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
|
private val ideaVimSupportDialog
|
||||||
val ideaVimSupportDialog =
|
get() = injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
||||||
injector.globalIjOptions().ideavimsupport.contains(IjOptionConstants.ideavimsupport_dialog)
|
|
||||||
|
|
||||||
|
override fun isHandlerEnabled(editor: Editor, dataContext: DataContext?): Boolean {
|
||||||
return editor.isPrimaryEditor() ||
|
return editor.isPrimaryEditor() ||
|
||||||
EditorHelper.isFileEditor(editor) && !editor.vim.mode.inNormalMode ||
|
EditorHelper.isFileEditor(editor) && vimStateNeedsToHandleEscape(editor) ||
|
||||||
ideaVimSupportDialog && !editor.vim.mode.inNormalMode
|
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.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.globalOptions
|
import com.maddyhome.idea.vim.api.globalOptions
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
|
import com.maddyhome.idea.vim.common.EditorListener
|
||||||
import com.maddyhome.idea.vim.common.IsReplaceCharListener
|
import com.maddyhome.idea.vim.common.IsReplaceCharListener
|
||||||
import com.maddyhome.idea.vim.common.ModeChangeListener
|
import com.maddyhome.idea.vim.common.ModeChangeListener
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
@@ -73,7 +74,7 @@ internal object GuicursorChangeListener : EffectiveOptionValueChangeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Editor.guicursorMode(): GuiCursorMode {
|
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() {
|
private fun Editor.updatePrimaryCaretVisualAttributes() {
|
||||||
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
|
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)
|
caretModel.primaryCaret.visualAttributes = AttributesCache.getCaretVisualAttributes(this)
|
||||||
|
|
||||||
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
// Make sure the caret is visible as soon as it's set. It might be invisible while blinking
|
||||||
@@ -99,6 +101,7 @@ private fun Editor.updatePrimaryCaretVisualAttributes() {
|
|||||||
|
|
||||||
private fun Editor.updateSecondaryCaretsVisualAttributes() {
|
private fun Editor.updateSecondaryCaretsVisualAttributes() {
|
||||||
if (VimPlugin.isNotEnabled()) thisLogger().error("The caret attributes should not be updated if the IdeaVim is disabled")
|
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
|
// 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)
|
val attributes = if (this.vim.inBlockSelection) HIDDEN else AttributesCache.getCaretVisualAttributes(this)
|
||||||
this.caretModel.allCarets.forEach {
|
this.caretModel.allCarets.forEach {
|
||||||
@@ -144,7 +147,7 @@ private object AttributesCache {
|
|||||||
@TestOnly
|
@TestOnly
|
||||||
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
internal fun getGuiCursorMode(editor: Editor) = editor.guicursorMode()
|
||||||
|
|
||||||
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener {
|
class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener, EditorListener {
|
||||||
override fun isReplaceCharChanged(editor: VimEditor) {
|
override fun isReplaceCharChanged(editor: VimEditor) {
|
||||||
updateCaretsVisual(editor)
|
updateCaretsVisual(editor)
|
||||||
}
|
}
|
||||||
@@ -153,21 +156,19 @@ class CaretVisualAttributesListener : IsReplaceCharListener, ModeChangeListener
|
|||||||
updateCaretsVisual(editor)
|
updateCaretsVisual(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun focusGained(editor: VimEditor) {
|
||||||
|
updateCaretsVisual(editor)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateCaretsVisual(editor: VimEditor) {
|
private fun updateCaretsVisual(editor: VimEditor) {
|
||||||
if (injector.globalOptions().ideaglobalmode) {
|
|
||||||
updateAllEditorsCaretsVisual()
|
|
||||||
} else {
|
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
val ijEditor = (editor as IjVimEditor).editor
|
||||||
ijEditor.updateCaretsVisualAttributes()
|
ijEditor.updateCaretsVisualAttributes()
|
||||||
ijEditor.updateCaretsVisualPosition()
|
ijEditor.updateCaretsVisualPosition()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun updateAllEditorsCaretsVisual() {
|
fun updateAllEditorsCaretsVisual() {
|
||||||
injector.editorGroup.getEditors().forEach { editor ->
|
injector.editorGroup.getEditors().forEach { editor ->
|
||||||
val ijEditor = (editor as IjVimEditor).editor
|
updateCaretsVisual(editor)
|
||||||
ijEditor.updateCaretsVisualAttributes()
|
|
||||||
ijEditor.updateCaretsVisualPosition()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -34,4 +34,4 @@ val Editor.inVisualMode: Boolean
|
|||||||
|
|
||||||
@get:JvmName("inExMode")
|
@get:JvmName("inExMode")
|
||||||
internal val Editor.inExMode
|
internal val Editor.inExMode
|
||||||
get() = this.vim.vimStateMachine.mode is Mode.CMD_LINE
|
get() = this.vim.mode is Mode.CMD_LINE
|
||||||
|
@@ -157,6 +157,19 @@ public class EditorHelper {
|
|||||||
return (int)(getVisibleArea(editor).width / getPlainSpaceWidthFloat(editor));
|
return (int)(getVisibleArea(editor).width / getPlainSpaceWidthFloat(editor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of characters that can be fit inside the output panel for an editor.
|
||||||
|
* <p>
|
||||||
|
* This will be greater than the approximate screen width as it also includes any gutter components in the editor.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param editor The editor
|
||||||
|
* @return The approximate number of columns that can fit in the output panel
|
||||||
|
*/
|
||||||
|
public static int getApproximateOutputPanelWidth(final @NotNull Editor editor) {
|
||||||
|
return (int)(editor.getComponent().getWidth() / getPlainSpaceWidthFloat(editor));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the width of the space character in the editor's plain font as a float.
|
* Gets the width of the space character in the editor's plain font as a float.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -273,7 +286,7 @@ public class EditorHelper {
|
|||||||
|
|
||||||
// Scroll the given visual line to the caret location, but do not scroll down passed the end of file, or the current
|
// Scroll the given visual line to the caret location, but do not scroll down passed the end of file, or the current
|
||||||
// virtual space at the bottom of the screen
|
// virtual space at the bottom of the screen
|
||||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
final @NotNull VimEditor editor1 = new IjVimEditor(editor);
|
||||||
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
||||||
final int yBottomLineOffset = max(getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine), visibleArea.y);
|
final int yBottomLineOffset = max(getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine), visibleArea.y);
|
||||||
scrollVertically(editor, min(yVisualLine - caretScreenOffset - inlayOffset, yBottomLineOffset));
|
scrollVertically(editor, min(yVisualLine - caretScreenOffset - inlayOffset, yBottomLineOffset));
|
||||||
@@ -325,7 +338,7 @@ public class EditorHelper {
|
|||||||
final int lineHeight = editor.getLineHeight();
|
final int lineHeight = editor.getLineHeight();
|
||||||
|
|
||||||
final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
|
final int offset = y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight);
|
||||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
final @NotNull VimEditor editor1 = new IjVimEditor(editor);
|
||||||
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount();
|
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) + editor.getSettings().getAdditionalLinesCount();
|
||||||
final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
|
final int offsetForLastLineAtBottom = getOffsetToScrollVisualLineToBottomOfScreen(editor, lastVisualLine);
|
||||||
|
|
||||||
@@ -379,7 +392,7 @@ public class EditorHelper {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getHorizontalScrollbarHeight(@NotNull final Editor editor) {
|
private static int getHorizontalScrollbarHeight(final @NotNull Editor editor) {
|
||||||
// Horizontal scrollbars on macOS are either transparent AND auto-hide, so we don't need to worry about obscured
|
// Horizontal scrollbars on macOS are either transparent AND auto-hide, so we don't need to worry about obscured
|
||||||
// text, or always visible, opaque and outside the content area, so we don't need to adjust for them
|
// text, or always visible, opaque and outside the content area, so we don't need to adjust for them
|
||||||
// Transparent scrollbars on Windows and Linux are overlays on the editor content area, and always visible. That
|
// Transparent scrollbars on Windows and Linux are overlays on the editor content area, and always visible. That
|
||||||
@@ -462,7 +475,7 @@ public class EditorHelper {
|
|||||||
*/
|
*/
|
||||||
public static Pair<Boolean, Integer> scrollFullPageDown(final @NotNull Editor editor, int pages) {
|
public static Pair<Boolean, Integer> scrollFullPageDown(final @NotNull Editor editor, int pages) {
|
||||||
final Rectangle visibleArea = getVisibleArea(editor);
|
final Rectangle visibleArea = getVisibleArea(editor);
|
||||||
@NotNull final VimEditor editor2 = new IjVimEditor(editor);
|
final @NotNull VimEditor editor2 = new IjVimEditor(editor);
|
||||||
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor2) - 1;
|
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor2) - 1;
|
||||||
|
|
||||||
int y = visibleArea.y + visibleArea.height;
|
int y = visibleArea.y + visibleArea.height;
|
||||||
@@ -480,7 +493,7 @@ public class EditorHelper {
|
|||||||
caretVisualLine = lastVisualLine;
|
caretVisualLine = lastVisualLine;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
final @NotNull VimEditor editor1 = new IjVimEditor(editor);
|
||||||
caretVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
caretVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
||||||
completed = false;
|
completed = false;
|
||||||
}
|
}
|
||||||
@@ -515,7 +528,7 @@ public class EditorHelper {
|
|||||||
public static Pair<Boolean, Integer> scrollFullPageUp(final @NotNull Editor editor, int pages) {
|
public static Pair<Boolean, Integer> scrollFullPageUp(final @NotNull Editor editor, int pages) {
|
||||||
final Rectangle visibleArea = getVisibleArea(editor);
|
final Rectangle visibleArea = getVisibleArea(editor);
|
||||||
final int lineHeight = editor.getLineHeight();
|
final int lineHeight = editor.getLineHeight();
|
||||||
@NotNull final VimEditor editor1 = new IjVimEditor(editor);
|
final @NotNull VimEditor editor1 = new IjVimEditor(editor);
|
||||||
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
final int lastVisualLine = EngineEditorHelperKt.getVisualLineCount(editor1) - 1;
|
||||||
|
|
||||||
int y = visibleArea.y;
|
int y = visibleArea.y;
|
||||||
|
@@ -13,23 +13,23 @@ import com.intellij.openapi.actionSystem.ActionManager
|
|||||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
import com.intellij.openapi.actionSystem.AnAction
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.AnActionResult
|
|
||||||
import com.intellij.openapi.actionSystem.DataContextWrapper
|
import com.intellij.openapi.actionSystem.DataContextWrapper
|
||||||
import com.intellij.openapi.actionSystem.EmptyAction
|
import com.intellij.openapi.actionSystem.EmptyAction
|
||||||
import com.intellij.openapi.actionSystem.IdeActions
|
import com.intellij.openapi.actionSystem.IdeActions
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
|
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||||
|
import com.intellij.openapi.actionSystem.ex.ActionUtil.performDumbAwareWithCallbacks
|
||||||
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
import com.intellij.openapi.actionSystem.impl.ProxyShortcutSet
|
||||||
import com.intellij.openapi.actionSystem.impl.Utils
|
import com.intellij.openapi.actionSystem.impl.Utils
|
||||||
|
import com.intellij.openapi.application.ex.ApplicationManagerEx
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
import com.intellij.openapi.command.UndoConfirmationPolicy
|
import com.intellij.openapi.command.UndoConfirmationPolicy
|
||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
|
import com.intellij.openapi.diagnostic.thisLogger
|
||||||
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
|
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId
|
||||||
import com.intellij.openapi.project.IndexNotReadyException
|
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
|
||||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||||
import com.intellij.openapi.util.NlsContexts
|
import com.intellij.openapi.util.NlsContexts
|
||||||
import com.intellij.util.SlowOperations
|
|
||||||
import com.maddyhome.idea.vim.RegisterActions
|
import com.maddyhome.idea.vim.RegisterActions
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.NativeAction
|
import com.maddyhome.idea.vim.api.NativeAction
|
||||||
@@ -61,6 +61,9 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
get() = IdeActions.ACTION_EXPAND_REGION
|
get() = IdeActions.ACTION_EXPAND_REGION
|
||||||
override val ACTION_EXPAND_REGION_RECURSIVELY: String
|
override val ACTION_EXPAND_REGION_RECURSIVELY: String
|
||||||
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
|
get() = IdeActions.ACTION_EXPAND_REGION_RECURSIVELY
|
||||||
|
override val ACTION_EXPAND_COLLAPSE_TOGGLE: String
|
||||||
|
// [VERSION UPDATE] 2024.3+ Replace raw "ExpandCollapseToggleAction" with IdeActions.ACTION_EXPAND_COLLAPSE_TOGGLE_REGION from the platform.
|
||||||
|
get() = "ExpandCollapseToggleAction"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute an action
|
* Execute an action
|
||||||
@@ -69,6 +72,12 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
* @param context The context to run it in
|
* @param context The context to run it in
|
||||||
*/
|
*/
|
||||||
override fun executeAction(editor: VimEditor?, action: NativeAction, context: ExecutionContext): Boolean {
|
override fun executeAction(editor: VimEditor?, action: NativeAction, context: ExecutionContext): Boolean {
|
||||||
|
val applicationEx = ApplicationManagerEx.getApplicationEx()
|
||||||
|
if (ProgressIndicatorUtils.isWriteActionRunningOrPending(applicationEx)) {
|
||||||
|
// This is needed for VIM-3376 and it should turn into error at soeme moment
|
||||||
|
thisLogger().warn("Actions cannot be updated when write-action is running or pending")
|
||||||
|
}
|
||||||
|
|
||||||
val ijAction = (action as IjNativeAction).action
|
val ijAction = (action as IjNativeAction).action
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,51 +130,15 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is taken directly from ActionUtil.performActionDumbAwareWithCallbacks
|
|
||||||
// But with one check removed. With this check some actions (like `:w` doesn't work)
|
|
||||||
// https://youtrack.jetbrains.com/issue/VIM-2691/File-is-not-saved-on-w
|
|
||||||
private fun performDumbAwareWithCallbacks(
|
|
||||||
action: AnAction,
|
|
||||||
event: AnActionEvent,
|
|
||||||
performRunnable: Runnable,
|
|
||||||
) {
|
|
||||||
val project = event.project
|
|
||||||
var indexError: IndexNotReadyException? = null
|
|
||||||
val manager = ActionManagerEx.getInstanceEx()
|
|
||||||
manager.fireBeforeActionPerformed(action, event)
|
|
||||||
var result: AnActionResult? = null
|
|
||||||
try {
|
|
||||||
SlowOperations.allowSlowOperations(SlowOperations.ACTION_PERFORM).use {
|
|
||||||
performRunnable.run()
|
|
||||||
result = AnActionResult.PERFORMED
|
|
||||||
}
|
|
||||||
} catch (ex: IndexNotReadyException) {
|
|
||||||
indexError = ex
|
|
||||||
result = AnActionResult.failed(ex)
|
|
||||||
} catch (ex: RuntimeException) {
|
|
||||||
result = AnActionResult.failed(ex)
|
|
||||||
throw ex
|
|
||||||
} catch (ex: Error) {
|
|
||||||
result = AnActionResult.failed(ex)
|
|
||||||
throw ex
|
|
||||||
} finally {
|
|
||||||
if (result == null) result = AnActionResult.failed(Throwable())
|
|
||||||
manager.fireAfterActionPerformed(action, event, result!!)
|
|
||||||
}
|
|
||||||
if (indexError != null) {
|
|
||||||
ActionUtil.showDumbModeWarning(project, action, event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute an action by name
|
* Execute an action by name
|
||||||
*
|
*
|
||||||
* @param name The name of the action to execute
|
* @param name The name of the action to execute
|
||||||
* @param context The context to run it in
|
* @param context The context to run it in
|
||||||
*/
|
*/
|
||||||
override fun executeAction(name: @NonNls String, context: ExecutionContext): Boolean {
|
override fun executeAction(editor: VimEditor, name: @NonNls String, context: ExecutionContext): Boolean {
|
||||||
val action = getAction(name, context)
|
val action = getAction(name, context)
|
||||||
return action != null && executeAction(null, IjNativeAction(action), context)
|
return action != null && executeAction(editor, IjNativeAction(action), context)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAction(name: String, context: ExecutionContext): AnAction? {
|
private fun getAction(name: String, context: ExecutionContext): AnAction? {
|
||||||
@@ -211,8 +184,8 @@ internal class IjActionExecutor : VimActionExecutor {
|
|||||||
CommandProcessor.getInstance().executeCommand(editor?.ij?.project, runnable, name, groupId)
|
CommandProcessor.getInstance().executeCommand(editor?.ij?.project, runnable, name, groupId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun executeEsc(context: ExecutionContext): Boolean {
|
override fun executeEsc(editor: VimEditor, context: ExecutionContext): Boolean {
|
||||||
return executeAction(IdeActions.ACTION_EDITOR_ESCAPE, context)
|
return executeAction(editor, IdeActions.ACTION_EDITOR_ESCAPE, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun executeVimAction(
|
override fun executeVimAction(
|
||||||
|
@@ -13,7 +13,7 @@ import com.intellij.openapi.editor.ReadOnlyFragmentModificationException
|
|||||||
import com.intellij.openapi.editor.VisualPosition
|
import com.intellij.openapi.editor.VisualPosition
|
||||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
||||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||||
import com.maddyhome.idea.vim.api.EngineEditorHelper
|
import com.maddyhome.idea.vim.api.EngineEditorHelperBase
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimRangeMarker
|
import com.maddyhome.idea.vim.api.VimRangeMarker
|
||||||
import com.maddyhome.idea.vim.api.VimVisualPosition
|
import com.maddyhome.idea.vim.api.VimVisualPosition
|
||||||
@@ -22,7 +22,7 @@ import com.maddyhome.idea.vim.newapi.ij
|
|||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
internal class IjEditorHelper : EngineEditorHelper {
|
internal class IjEditorHelper : EngineEditorHelperBase() {
|
||||||
override fun amountOfInlaysBeforeVisualPosition(editor: VimEditor, pos: VimVisualPosition): Int {
|
override fun amountOfInlaysBeforeVisualPosition(editor: VimEditor, pos: VimVisualPosition): Int {
|
||||||
return (editor as IjVimEditor).editor.amountOfInlaysBeforeVisualPosition(
|
return (editor as IjVimEditor).editor.amountOfInlaysBeforeVisualPosition(
|
||||||
VisualPosition(
|
VisualPosition(
|
||||||
@@ -41,6 +41,10 @@ internal class IjEditorHelper : EngineEditorHelper {
|
|||||||
return EditorHelper.getApproximateScreenWidth(editor.ij)
|
return EditorHelper.getApproximateScreenWidth(editor.ij)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getApproximateOutputPanelWidth(editor: VimEditor): Int {
|
||||||
|
return EditorHelper.getApproximateOutputPanelWidth(editor.ij)
|
||||||
|
}
|
||||||
|
|
||||||
override fun handleWithReadonlyFragmentModificationHandler(editor: VimEditor, exception: Exception) {
|
override fun handleWithReadonlyFragmentModificationHandler(editor: VimEditor, exception: Exception) {
|
||||||
return EditorActionManager.getInstance()
|
return EditorActionManager.getInstance()
|
||||||
.getReadonlyFragmentModificationHandler(editor.ij.document)
|
.getReadonlyFragmentModificationHandler(editor.ij.document)
|
||||||
@@ -51,10 +55,6 @@ internal class IjEditorHelper : EngineEditorHelper {
|
|||||||
return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
|
return EditorHelper.getVisualLineAtBottomOfScreen(editor.ij)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pad(editor: VimEditor, line: Int, to: Int): String {
|
|
||||||
return EditorHelper.pad(editor.ij, line, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
|
override fun inlayAwareOffsetToVisualPosition(editor: VimEditor, offset: Int): VimVisualPosition {
|
||||||
return EditorUtil.inlayAwareOffsetToVisualPosition(editor.ij, offset).vim
|
return EditorUtil.inlayAwareOffsetToVisualPosition(editor.ij, offset).vim
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,6 @@ import com.maddyhome.idea.vim.VimPlugin
|
|||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||||
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
import com.maddyhome.idea.vim.api.getLineStartForOffset
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
|
||||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
|
||||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||||
@@ -31,7 +30,7 @@ import com.maddyhome.idea.vim.state.mode.returnTo
|
|||||||
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
|
internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
|
||||||
if (!this.vim.inSelectMode) return
|
if (!this.vim.inSelectMode) return
|
||||||
|
|
||||||
val returnTo = this.vim.vimStateMachine.mode.returnTo
|
val returnTo = this.vim.mode.returnTo
|
||||||
when (returnTo) {
|
when (returnTo) {
|
||||||
ReturnTo.INSERT -> {
|
ReturnTo.INSERT -> {
|
||||||
this.vim.mode = Mode.INSERT
|
this.vim.mode = Mode.INSERT
|
||||||
@@ -64,7 +63,7 @@ internal fun Editor.exitSelectMode(adjustCaretPosition: Boolean) {
|
|||||||
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
|
internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
|
||||||
if (!this.inSelectMode) return
|
if (!this.inSelectMode) return
|
||||||
|
|
||||||
val returnTo = this.vimStateMachine.mode.returnTo
|
val returnTo = this.mode.returnTo
|
||||||
when (returnTo) {
|
when (returnTo) {
|
||||||
ReturnTo.INSERT -> {
|
ReturnTo.INSERT -> {
|
||||||
this.mode = Mode.INSERT
|
this.mode = Mode.INSERT
|
||||||
@@ -94,6 +93,6 @@ internal fun VimEditor.exitSelectMode(adjustCaretPosition: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Editor.exitInsertMode(context: DataContext, operatorArguments: OperatorArguments) {
|
internal fun Editor.exitInsertMode(context: DataContext) {
|
||||||
VimPlugin.getChange().processEscape(IjVimEditor(this), IjEditorExecutionContext(context), operatorArguments)
|
VimPlugin.getChange().processEscape(IjVimEditor(this), IjEditorExecutionContext(context))
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,6 @@ import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToBottomOfScre
|
|||||||
import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToMiddleOfScreen
|
import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToMiddleOfScreen
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToTopOfScreen
|
import com.maddyhome.idea.vim.helper.EditorHelper.scrollVisualLineToTopOfScreen
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@@ -180,7 +179,7 @@ internal object ScrollViewHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getScrollJump(editor: VimEditor, height: Int): Int {
|
private fun getScrollJump(editor: VimEditor, height: Int): Int {
|
||||||
val flags = VimStateMachine.getInstance(editor).executingCommandFlags
|
val flags = injector.vimState.executingCommandFlags
|
||||||
val scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)
|
val scrollJump = !flags.contains(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)
|
||||||
|
|
||||||
// Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line
|
// Default value is 1. Zero is a valid value, but we normalise to 1 - we always want to scroll at least one line
|
||||||
@@ -203,7 +202,7 @@ internal object ScrollViewHelper {
|
|||||||
val caretColumn = position.column
|
val caretColumn = position.column
|
||||||
val halfWidth = getApproximateScreenWidth(editor) / 2
|
val halfWidth = getApproximateScreenWidth(editor) / 2
|
||||||
val scrollOffset = getNormalizedSideScrollOffset(editor)
|
val scrollOffset = getNormalizedSideScrollOffset(editor)
|
||||||
val flags = VimStateMachine.getInstance(vimEditor).executingCommandFlags
|
val flags = injector.vimState.executingCommandFlags
|
||||||
val allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP)
|
val allowSidescroll = !flags.contains(CommandFlags.FLAG_IGNORE_SIDE_SCROLL_JUMP)
|
||||||
val sidescroll = injector.options(vimEditor).sidescroll
|
val sidescroll = injector.options(vimEditor).sidescroll
|
||||||
val offsetLeft = caretColumn - (currentVisualLeftColumn + scrollOffset)
|
val offsetLeft = caretColumn - (currentVisualLeftColumn + scrollOffset)
|
||||||
|
@@ -16,18 +16,15 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
|
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||||
import com.maddyhome.idea.vim.api.getText
|
|
||||||
import com.maddyhome.idea.vim.api.globalOptions
|
import com.maddyhome.idea.vim.api.globalOptions
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
||||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntComparator
|
import it.unimi.dsi.fastutil.ints.IntComparator
|
||||||
import it.unimi.dsi.fastutil.ints.IntIterator
|
import it.unimi.dsi.fastutil.ints.IntIterator
|
||||||
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
|
import it.unimi.dsi.fastutil.ints.IntRBTreeSet
|
||||||
import it.unimi.dsi.fastutil.ints.IntSortedSet
|
import it.unimi.dsi.fastutil.ints.IntSortedSet
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check ignorecase and smartcase options to see if a case insensitive search should be performed with the given pattern.
|
* Check ignorecase and smartcase options to see if a case insensitive search should be performed with the given pattern.
|
||||||
@@ -97,210 +94,6 @@ fun countWords(
|
|||||||
return CountPosition(count, position)
|
return CountPosition(count, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findNumbersInRange(
|
|
||||||
editor: Editor,
|
|
||||||
textRange: TextRange,
|
|
||||||
alpha: Boolean,
|
|
||||||
hex: Boolean,
|
|
||||||
octal: Boolean,
|
|
||||||
): List<Pair<TextRange, NumberType>> {
|
|
||||||
val result: MutableList<Pair<TextRange, NumberType>> = ArrayList()
|
|
||||||
|
|
||||||
|
|
||||||
for (i in 0 until textRange.size()) {
|
|
||||||
val startOffset = textRange.startOffsets[i]
|
|
||||||
val end = textRange.endOffsets[i]
|
|
||||||
val text: String = editor.vim.getText(startOffset, end)
|
|
||||||
val textChunks = text.split("\\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
||||||
var chunkStart = 0
|
|
||||||
for (chunk in textChunks) {
|
|
||||||
val number = findNumberInText(chunk, 0, alpha, hex, octal)
|
|
||||||
|
|
||||||
if (number != null) {
|
|
||||||
result.add(
|
|
||||||
Pair(
|
|
||||||
TextRange(
|
|
||||||
number.first.startOffset + startOffset + chunkStart,
|
|
||||||
number.first.endOffset + startOffset + chunkStart
|
|
||||||
),
|
|
||||||
number.second
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
chunkStart += 1 + chunk.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findNumberUnderCursor(
|
|
||||||
editor: Editor,
|
|
||||||
caret: Caret,
|
|
||||||
alpha: Boolean,
|
|
||||||
hex: Boolean,
|
|
||||||
octal: Boolean,
|
|
||||||
): Pair<TextRange, NumberType>? {
|
|
||||||
val lline = caret.logicalPosition.line
|
|
||||||
val text = IjVimEditor(editor).getLineText(lline).lowercase(Locale.getDefault())
|
|
||||||
val startLineOffset = IjVimEditor(editor).getLineStartOffset(lline)
|
|
||||||
val posOnLine = caret.offset - startLineOffset
|
|
||||||
|
|
||||||
val numberTextRange = findNumberInText(text, posOnLine, alpha, hex, octal) ?: return null
|
|
||||||
|
|
||||||
return Pair(
|
|
||||||
TextRange(
|
|
||||||
numberTextRange.first.startOffset + startLineOffset,
|
|
||||||
numberTextRange.first.endOffset + startLineOffset
|
|
||||||
),
|
|
||||||
numberTextRange.second
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for number in given text from start position
|
|
||||||
*
|
|
||||||
* @param textInRange - text to search in
|
|
||||||
* @param startPosOnLine - start offset to search
|
|
||||||
* @return - text range with number
|
|
||||||
*/
|
|
||||||
fun findNumberInText(
|
|
||||||
textInRange: String,
|
|
||||||
startPosOnLine: Int,
|
|
||||||
alpha: Boolean,
|
|
||||||
hex: Boolean,
|
|
||||||
octal: Boolean,
|
|
||||||
): Pair<TextRange, NumberType>? {
|
|
||||||
if (logger.isDebugEnabled) {
|
|
||||||
logger.debug("text=$textInRange")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = startPosOnLine
|
|
||||||
val lineEndOffset = textInRange.length
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// Skip over current whitespace if any
|
|
||||||
while (pos < lineEndOffset && !isNumberChar(textInRange[pos], alpha, hex, octal, true)) {
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled) logger.debug("pos=$pos")
|
|
||||||
if (pos >= lineEndOffset) {
|
|
||||||
logger.debug("no number char on line")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val isHexChar = "abcdefABCDEF".indexOf(textInRange[pos]) >= 0
|
|
||||||
|
|
||||||
if (hex) {
|
|
||||||
// Ox and OX handling
|
|
||||||
if (textInRange[pos] == '0' && pos < lineEndOffset - 1 && "xX".indexOf(textInRange[pos + 1]) >= 0) {
|
|
||||||
pos += 2
|
|
||||||
} else if ("xX".indexOf(textInRange[pos]) >= 0 && pos > 0 && textInRange[pos - 1] == '0') {
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("checking hex")
|
|
||||||
val range = findRange(textInRange, pos, false, true, false, false)
|
|
||||||
val start = range.first
|
|
||||||
val end = range.second
|
|
||||||
|
|
||||||
// Ox and OX
|
|
||||||
if (start >= 2 && textInRange.substring(start - 2, start).equals("0x", ignoreCase = true)) {
|
|
||||||
logger.debug("found hex")
|
|
||||||
return Pair(TextRange(start - 2, end), NumberType.HEX)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isHexChar || alpha) {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (octal) {
|
|
||||||
logger.debug("checking octal")
|
|
||||||
val range = findRange(textInRange, pos, false, false, true, false)
|
|
||||||
val start = range.first
|
|
||||||
val end = range.second
|
|
||||||
|
|
||||||
if (end - start == 1 && textInRange[start] == '0') {
|
|
||||||
return Pair(TextRange(start, end), NumberType.DEC)
|
|
||||||
}
|
|
||||||
if (textInRange[start] == '0' && end > start &&
|
|
||||||
!(start > 0 && isNumberChar(textInRange[start - 1], false, false, false, true))
|
|
||||||
) {
|
|
||||||
logger.debug("found octal")
|
|
||||||
return Pair(TextRange(start, end), NumberType.OCT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alpha) {
|
|
||||||
if (logger.isDebugEnabled) logger.debug("checking alpha for " + textInRange[pos])
|
|
||||||
if (isNumberChar(textInRange[pos], true, false, false, false)) {
|
|
||||||
if (logger.isDebugEnabled) logger.debug("found alpha at $pos")
|
|
||||||
return Pair(TextRange(pos, pos + 1), NumberType.ALPHA)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val range = findRange(textInRange, pos, false, false, false, true)
|
|
||||||
var start = range.first
|
|
||||||
val end = range.second
|
|
||||||
if (start > 0 && textInRange[start - 1] == '-') {
|
|
||||||
start--
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pair(TextRange(start, end), NumberType.DEC)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for digits block that matches parameters
|
|
||||||
*/
|
|
||||||
private fun findRange(
|
|
||||||
text: String,
|
|
||||||
pos: Int,
|
|
||||||
alpha: Boolean,
|
|
||||||
hex: Boolean,
|
|
||||||
octal: Boolean,
|
|
||||||
decimal: Boolean,
|
|
||||||
): Pair<Int, Int> {
|
|
||||||
var end = pos
|
|
||||||
while (end < text.length && isNumberChar(text[end], alpha, hex, octal, decimal || octal)) {
|
|
||||||
end++
|
|
||||||
}
|
|
||||||
var start = pos
|
|
||||||
while (start >= 0 && isNumberChar(text[start], alpha, hex, octal, decimal || octal)) {
|
|
||||||
start--
|
|
||||||
}
|
|
||||||
if (start < end &&
|
|
||||||
(start == -1 ||
|
|
||||||
0 <= start && start < text.length &&
|
|
||||||
!isNumberChar(text[start], alpha, hex, octal, decimal || octal))
|
|
||||||
) {
|
|
||||||
start++
|
|
||||||
}
|
|
||||||
if (octal) {
|
|
||||||
for (i in start until end) {
|
|
||||||
if (!isNumberChar(text[i], false, false, true, false)) return Pair(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Pair(start, end)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isNumberChar(ch: Char, alpha: Boolean, hex: Boolean, octal: Boolean, decimal: Boolean): Boolean {
|
|
||||||
return if (alpha && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
|
|
||||||
true
|
|
||||||
} else if (octal && (ch >= '0' && ch <= '7')) {
|
|
||||||
true
|
|
||||||
} else if (hex && ((ch >= '0' && ch <= '9') || "abcdefABCDEF".indexOf(ch) >= 0)) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
decimal && (ch >= '0' && ch <= '9')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the word under the cursor or the next word to the right of the cursor on the current line.
|
* Find the word under the cursor or the next word to the right of the cursor on the current line.
|
||||||
*
|
*
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user