mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
538 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
c79286b9b0
|
|||
5f59b47b19
|
|||
8d51537f79
|
|||
052de10e3a
|
|||
9ece9a7a04
|
|||
84c868afc3
|
|||
f29ebab390
|
|||
0cb8bba3fd
|
|||
c0ff2b5cd0
|
|||
460234553d
|
|||
cdd5b2abaf
|
|||
9db1732eb3
|
|||
63e292b21f
|
|||
362175431d
|
|||
5e2cab4eda
|
|||
b63792c8f8
|
|||
f543b6a1d1
|
|||
d367b3bc72
|
|||
da2d8d707f
|
|||
![]() |
75a417773f | ||
![]() |
b3b3ee4f21 | ||
![]() |
07b1db4b28 | ||
![]() |
dc775a0f22 | ||
![]() |
10228f953e | ||
![]() |
afceecadbe | ||
![]() |
b2a4e59571 | ||
![]() |
b0b944bbf3 | ||
![]() |
89a3d74b93 | ||
![]() |
f4eef04750 | ||
![]() |
e62c86b99f | ||
![]() |
82bd792da5 | ||
![]() |
a58c9065e6 | ||
![]() |
e8bf984b76 | ||
![]() |
23e1a3499f | ||
![]() |
6b4e4bacd7 | ||
![]() |
a84c04ca08 | ||
![]() |
e67c71e440 | ||
![]() |
5078ff9c7a | ||
![]() |
647510de5d | ||
![]() |
84e11e4236 | ||
![]() |
9538714af1 | ||
![]() |
ffd832d990 | ||
![]() |
8de2b8976b | ||
![]() |
a6aa26b5d9 | ||
![]() |
2505651c68 | ||
![]() |
e67c7b23ff | ||
![]() |
453cca3b0c | ||
![]() |
6cee04a4be | ||
![]() |
ae8b9b4773 | ||
![]() |
e748b7b265 | ||
![]() |
c2401ec013 | ||
![]() |
8073d7ecd0 | ||
![]() |
64f7859ba7 | ||
![]() |
f1b94d7026 | ||
![]() |
79653b6048 | ||
![]() |
b0e6b72281 | ||
![]() |
e6220e5e53 | ||
![]() |
3c064845b1 | ||
![]() |
736cb219ca | ||
![]() |
fb30e4e387 | ||
![]() |
74550ffa16 | ||
![]() |
d0a0672282 | ||
![]() |
16e92ddf60 | ||
![]() |
4d8e68d800 | ||
![]() |
bbebfaf32a | ||
![]() |
7e56331e47 | ||
![]() |
750db8e71c | ||
![]() |
4255ef68a3 | ||
![]() |
3313464214 | ||
![]() |
683ba32a15 | ||
![]() |
90a60155e5 | ||
![]() |
b25d06ed9e | ||
![]() |
706ae3dd91 | ||
![]() |
9b15ed8181 | ||
![]() |
f355bef36b | ||
![]() |
4391e69c48 | ||
![]() |
0710d80391 | ||
![]() |
cf41a3a76c | ||
![]() |
31b2cd872f | ||
![]() |
2b6945cbb2 | ||
![]() |
ae5f43918f | ||
![]() |
6b6bc2752e | ||
![]() |
4556adae3c | ||
![]() |
1b0886041b | ||
![]() |
16e18f3ca7 | ||
![]() |
ee0d67fbbb | ||
![]() |
450527f172 | ||
![]() |
135518ee39 | ||
![]() |
58715ecb5f | ||
![]() |
75e26b101d | ||
![]() |
6421a6face | ||
![]() |
948520f90a | ||
![]() |
0765118ce2 | ||
![]() |
efd4c7b617 | ||
![]() |
c5346fbece | ||
![]() |
fe8e8ccc3e | ||
![]() |
eae111bc2c | ||
![]() |
6a6c1dc6b4 | ||
![]() |
86bbb282ab | ||
![]() |
28aa156cb7 | ||
![]() |
a7814e69de | ||
![]() |
1452c116cf | ||
![]() |
23dfc4b339 | ||
![]() |
931d4be972 | ||
![]() |
7dceda587b | ||
![]() |
52a969074d | ||
![]() |
e7b87d31cf | ||
![]() |
5eb0fae08f | ||
![]() |
798d805a0f | ||
![]() |
0d4ba06e57 | ||
![]() |
4913b13a2d | ||
![]() |
b0bab992db | ||
![]() |
af5f4227b7 | ||
![]() |
fa6a694ea4 | ||
![]() |
1da7ffc052 | ||
![]() |
c673f5818c | ||
![]() |
ec78a87644 | ||
![]() |
69d14ddcf5 | ||
![]() |
f62819df00 | ||
![]() |
39a85b6bc2 | ||
![]() |
f76ae3e867 | ||
![]() |
3651e5f2f0 | ||
![]() |
89e016ef6c | ||
![]() |
e4996f4c4d | ||
![]() |
c44ed58142 | ||
![]() |
0091af2a41 | ||
![]() |
d1eea68719 | ||
![]() |
133aff7fd8 | ||
![]() |
efde94db7a | ||
![]() |
6ec072b34e | ||
![]() |
4027a21514 | ||
![]() |
3665b1ab00 | ||
![]() |
cf6b292f0c | ||
![]() |
507e4173d3 | ||
![]() |
abc3575d3e | ||
![]() |
2c0ff587e3 | ||
![]() |
26c87535d6 | ||
![]() |
6ac8e672be | ||
![]() |
04ee2dd1e7 | ||
![]() |
3106a98aee | ||
![]() |
73769a3472 | ||
![]() |
085e253d77 | ||
![]() |
b2af8f153e | ||
![]() |
37fb41fca8 | ||
![]() |
e2b05ab639 | ||
![]() |
354fd8fef0 | ||
![]() |
9b97867be1 | ||
![]() |
06685d1721 | ||
![]() |
ae4b88a06b | ||
![]() |
c83ecc46ed | ||
![]() |
c32050a208 | ||
![]() |
4a8c7227e6 | ||
![]() |
55e61a7094 | ||
![]() |
60977d05b6 | ||
![]() |
601747f720 | ||
![]() |
0c91bc3207 | ||
![]() |
f5cd2c173f | ||
![]() |
d00bd8bb25 | ||
![]() |
a66e44d835 | ||
![]() |
ce05317634 | ||
![]() |
33d88d55c9 | ||
![]() |
a31a4a8ca7 | ||
![]() |
c34d000b91 | ||
![]() |
a2bfe950fa | ||
![]() |
b44f40acd6 | ||
![]() |
5169093bbf | ||
![]() |
b15b1cd3f8 | ||
![]() |
f7b6a97435 | ||
![]() |
f64c99c406 | ||
![]() |
f552e43c5b | ||
![]() |
4798198e41 | ||
![]() |
a59de4ce05 | ||
![]() |
2dea525665 | ||
![]() |
8ecb1f7296 | ||
![]() |
9ca9530061 | ||
![]() |
54e27afc3f | ||
![]() |
5e67032655 | ||
![]() |
cb37f6df63 | ||
![]() |
ca3a18cf37 | ||
![]() |
cac36627aa | ||
![]() |
6404e1127e | ||
![]() |
e762a3093b | ||
![]() |
739ac2ae5e | ||
![]() |
77c364a2d0 | ||
![]() |
103cd9f5ce | ||
![]() |
99859fe857 | ||
![]() |
4778995f3b | ||
![]() |
0a14150840 | ||
![]() |
e8ffc0313f | ||
![]() |
7dbd3886b1 | ||
![]() |
8c83164d76 | ||
![]() |
076aab1ccf | ||
![]() |
751f51c88f | ||
![]() |
ab7359ffd3 | ||
![]() |
d795d70041 | ||
![]() |
1320034e7e | ||
![]() |
79a216043f | ||
![]() |
c508a3cc32 | ||
![]() |
d40d8f34c5 | ||
![]() |
b561a13e7c | ||
![]() |
71bcee2a07 | ||
![]() |
6c490b1baa | ||
![]() |
4ab6a36aca | ||
![]() |
c4355841ad | ||
![]() |
f163946e38 | ||
![]() |
7abb1fd630 | ||
![]() |
f78d3387a5 | ||
![]() |
69e2e3c047 | ||
![]() |
52832cbe22 | ||
![]() |
82ca2be51c | ||
![]() |
8552bb8a17 | ||
![]() |
b49a38b6d0 | ||
![]() |
ad0751677a | ||
![]() |
dd8bb20ba9 | ||
![]() |
03f5f27901 | ||
![]() |
9e3ca56afd | ||
![]() |
5379528e3e | ||
![]() |
12201359bc | ||
![]() |
ba50fcf85e | ||
![]() |
24c0db563c | ||
![]() |
33c4905dcb | ||
![]() |
9296c3f9a0 | ||
![]() |
7ecc566169 | ||
![]() |
a918da36d3 | ||
![]() |
91ff5fdb02 | ||
![]() |
4481631547 | ||
![]() |
7865388086 | ||
![]() |
26297bc5a9 | ||
![]() |
9db5cdd8e3 | ||
![]() |
2b17534a08 | ||
![]() |
4e2db68acf | ||
![]() |
ddabf8df5e | ||
![]() |
3f28e197ca | ||
![]() |
3a67524e8a | ||
![]() |
33312d95b0 | ||
![]() |
f554b21cd9 | ||
![]() |
d9fa4e4648 | ||
![]() |
e98a284d40 | ||
![]() |
38ef8c1053 | ||
![]() |
d3f560a31c | ||
![]() |
e2cb3ff284 | ||
![]() |
0f5a3953e1 | ||
![]() |
633667ed7f | ||
![]() |
656e975562 | ||
![]() |
f7fbe89de4 | ||
![]() |
509829b052 | ||
![]() |
efd61e17f2 | ||
![]() |
cf2b021d02 | ||
![]() |
67f10aece5 | ||
![]() |
9b5c777fcf | ||
![]() |
c65e5f8341 | ||
![]() |
053bb603cd | ||
![]() |
93b07bbb90 | ||
![]() |
764a115601 | ||
![]() |
1adfe53f29 | ||
![]() |
bfe0f51cb1 | ||
![]() |
ffce61906a | ||
![]() |
3843a193cb | ||
![]() |
b2410dab0b | ||
![]() |
f382544101 | ||
![]() |
a81d54eb90 | ||
![]() |
23519bbdae | ||
![]() |
73fd25773a | ||
![]() |
746d483179 | ||
![]() |
1a7e90c7d7 | ||
![]() |
8e2fd44f55 | ||
![]() |
1c84917b29 | ||
![]() |
c5522ed19d | ||
![]() |
33a5480456 | ||
![]() |
16cd05fc76 | ||
![]() |
4788dacc86 | ||
![]() |
91e54c8b0d | ||
![]() |
15ccebfe11 | ||
![]() |
5c849c9105 | ||
![]() |
9e99506223 | ||
![]() |
53ea90379f | ||
![]() |
2619d7ebb4 | ||
![]() |
8c8a7aceca | ||
![]() |
726b885b60 | ||
![]() |
a6994a09c3 | ||
![]() |
1616aff5a1 | ||
![]() |
5a82f05be8 | ||
![]() |
3020504cdc | ||
![]() |
d00e802674 | ||
![]() |
c46008dddc | ||
![]() |
755000c376 | ||
![]() |
1e4a60bfe6 | ||
![]() |
c9ee685956 | ||
![]() |
c2b1083836 | ||
![]() |
3518528f22 | ||
![]() |
9bed8fec79 | ||
![]() |
3ffbea7d42 | ||
![]() |
39b42193cb | ||
![]() |
c69e080b05 | ||
![]() |
50b42036d1 | ||
![]() |
340c40ceff | ||
![]() |
6aeffe71de | ||
![]() |
04c33d719e | ||
![]() |
07f1d1e8e6 | ||
![]() |
6c61254c50 | ||
![]() |
c6efea8c34 | ||
![]() |
80f43a7c66 | ||
![]() |
9f5d3a9a28 | ||
![]() |
8800a4f70b | ||
![]() |
990254dcac | ||
![]() |
73a1118e78 | ||
![]() |
d3bedf26cf | ||
![]() |
25b29f429a | ||
![]() |
363f821962 | ||
![]() |
8d873c0bf1 | ||
![]() |
955676ed9e | ||
![]() |
a2439a37e4 | ||
![]() |
7b6daa8e4e | ||
![]() |
fcf782296a | ||
![]() |
44889b635c | ||
![]() |
bc12ba11cd | ||
![]() |
9b08db75e3 | ||
4c3ad3f263 | |||
![]() |
598bcc7edd | ||
![]() |
b86ec03dc4 | ||
![]() |
ae75498f8a | ||
![]() |
9d0b68b0f8 | ||
![]() |
eeb5939e59 | ||
![]() |
ef235a47bf | ||
![]() |
b66da76880 | ||
![]() |
54d6119784 | ||
![]() |
0b8c081425 | ||
![]() |
209052ffa6 | ||
![]() |
fe9a6b5cfb | ||
![]() |
9c0f74369f | ||
![]() |
cd27e5229b | ||
![]() |
472732905c | ||
![]() |
485d9f81cd | ||
![]() |
8cf136ce4c | ||
![]() |
116a8ac9d2 | ||
![]() |
fda310bda6 | ||
![]() |
e55619ea33 | ||
![]() |
b952b20128 | ||
![]() |
62d1f85648 | ||
![]() |
5e3c8c0e92 | ||
![]() |
b58dddf2ff | ||
![]() |
78d351a0b0 | ||
![]() |
61dbc948cc | ||
![]() |
c4d92ebe73 | ||
![]() |
d0cf827638 | ||
![]() |
6a6a92b6b9 | ||
![]() |
9869b8a34e | ||
![]() |
60fbf88322 | ||
![]() |
fae3924062 | ||
![]() |
dc2ce64823 | ||
![]() |
d0d86d9178 | ||
![]() |
f417af6148 | ||
![]() |
2fe2860a09 | ||
![]() |
cb40426976 | ||
![]() |
423ed390a2 | ||
![]() |
7652b16ca6 | ||
![]() |
618a010c15 | ||
![]() |
d44a34ed9b | ||
![]() |
c84fc996db | ||
![]() |
43f232543b | ||
![]() |
3f65d1d99a | ||
![]() |
bfcf706ca7 | ||
![]() |
8c1103c461 | ||
![]() |
ab75ace8db | ||
![]() |
4a58e6a282 | ||
![]() |
ac9e4f69b4 | ||
![]() |
581edba7fd | ||
![]() |
58a8b96c3c | ||
![]() |
0e057ca9ae | ||
![]() |
36bf2639bb | ||
![]() |
0c1326e689 | ||
![]() |
dd74438f68 | ||
![]() |
a9ddfac782 | ||
![]() |
79437df894 | ||
![]() |
b5a04af089 | ||
![]() |
52372ae3d3 | ||
![]() |
65d755d9b2 | ||
![]() |
1f1a8f3395 | ||
![]() |
629e4e7053 | ||
![]() |
c50a299cfd | ||
![]() |
4bad129caf | ||
![]() |
1ffb28e21b | ||
![]() |
c126243367 | ||
![]() |
6da6e461a8 | ||
![]() |
103101bbcb | ||
![]() |
f737fcba1a | ||
![]() |
c5fa0678b8 | ||
![]() |
00ccddf8cf | ||
![]() |
00cbf188fb | ||
![]() |
988ea74461 | ||
![]() |
0914cda7e5 | ||
![]() |
5959e9aaa1 | ||
![]() |
434df565ae | ||
![]() |
c8f36504d8 | ||
![]() |
06e1af371e | ||
![]() |
d744987ac8 | ||
![]() |
b4eef17aaa | ||
![]() |
5c50e8607c | ||
![]() |
9a324ab448 | ||
![]() |
c3978335f5 | ||
![]() |
051296c2aa | ||
![]() |
90f2d2ff29 | ||
![]() |
4c2edab406 | ||
![]() |
76e8fd69bf | ||
![]() |
5dd458bcf7 | ||
![]() |
a94a8b8539 | ||
![]() |
261230b23a | ||
![]() |
b90317e00e | ||
![]() |
21c9dc8785 | ||
![]() |
31bbc60325 | ||
![]() |
fec6e5c189 | ||
![]() |
23c1493f17 | ||
![]() |
00808af569 | ||
![]() |
3c94091d30 | ||
![]() |
b737362aba | ||
![]() |
db722fc4e5 | ||
![]() |
7d679e68dc | ||
![]() |
bc808403fb | ||
![]() |
9d6dc317a4 | ||
![]() |
cf29c50f31 | ||
![]() |
2a3c4cc441 | ||
![]() |
bd192561ae | ||
![]() |
66ff56a05e | ||
![]() |
def86d179e | ||
![]() |
3c9a343f8b | ||
![]() |
10b6b05fab | ||
![]() |
caa4ef736a | ||
![]() |
23702345a9 | ||
![]() |
ba89babd10 | ||
![]() |
2ce3fbd677 | ||
![]() |
d8de73a06d | ||
![]() |
8094e6711a | ||
![]() |
10edccc1d6 | ||
![]() |
247aaed188 | ||
![]() |
1a4333fa1b | ||
![]() |
8eaa6df318 | ||
![]() |
7523db186f | ||
![]() |
4aac113522 | ||
![]() |
795abd77a7 | ||
![]() |
38bc914504 | ||
![]() |
c8113eea83 | ||
![]() |
924b7418e8 | ||
![]() |
a7dfef61e9 | ||
![]() |
db35c979b4 | ||
![]() |
2de933c723 | ||
![]() |
d3704d602f | ||
![]() |
ea62f227bf | ||
![]() |
23fdadc32e | ||
![]() |
e9bf06686f | ||
![]() |
7842b155c1 | ||
![]() |
74a8277e10 | ||
![]() |
ddb1b80463 | ||
![]() |
eea3336934 | ||
![]() |
f801145712 | ||
![]() |
e033b08535 | ||
![]() |
1d9514a205 | ||
![]() |
6741120f19 | ||
![]() |
c501457322 | ||
![]() |
46425a24c3 | ||
![]() |
9826f0a7f0 | ||
![]() |
43175061e0 | ||
![]() |
0ab32cac34 | ||
![]() |
e3ec9c614b | ||
![]() |
f454d60234 | ||
![]() |
19fa00837c | ||
![]() |
275c5d28e1 | ||
![]() |
15ae069f6f | ||
![]() |
00f5541dc6 | ||
![]() |
02540eb303 | ||
![]() |
282e581bdb | ||
![]() |
31e7c49608 | ||
![]() |
7966a6dc91 | ||
![]() |
5fc2f04224 | ||
![]() |
6edfd8ed22 | ||
![]() |
363db05db7 | ||
![]() |
3738012dd6 | ||
![]() |
355cfe035d | ||
![]() |
6d01b5be77 | ||
![]() |
4938957483 | ||
![]() |
46f4fa7cdd | ||
![]() |
f696135f31 | ||
![]() |
52e0fcdc7d | ||
![]() |
ac17518a23 | ||
6dd924b2b2 | |||
![]() |
f439474b73 | ||
![]() |
d6cd92e256 | ||
![]() |
3a294268d9 | ||
![]() |
9b81c7e650 | ||
![]() |
e229fb3ad7 | ||
![]() |
720eae63fa | ||
![]() |
0df96a24bd | ||
![]() |
21a1588ede | ||
![]() |
7970006e8c | ||
![]() |
418d0cff7f | ||
![]() |
7284360774 | ||
![]() |
9fc3fadee8 | ||
![]() |
3d2db56f63 | ||
![]() |
e9c7cb8670 | ||
![]() |
87d19274c5 | ||
![]() |
3161bf8ffd | ||
![]() |
b68865587e | ||
![]() |
7dc0dbe944 | ||
![]() |
f50a363525 | ||
![]() |
57ad4c70d1 | ||
![]() |
d3d93b898f | ||
![]() |
7d8973edb2 | ||
![]() |
2302b576b0 | ||
f4782630d4 | |||
![]() |
8c1a2a686f | ||
32d5e1e6fa | |||
![]() |
a381a1cacc | ||
![]() |
73c3c9f7fe | ||
![]() |
67ef0a75d5 | ||
![]() |
328bc5e95a | ||
![]() |
7f8021e37e | ||
![]() |
9701b7e79b | ||
![]() |
7a52c6fec9 | ||
![]() |
1503639d4b | ||
![]() |
e82f19c852 | ||
![]() |
edd69c9c25 | ||
![]() |
fc61e369fb | ||
![]() |
113586b59b | ||
![]() |
5dbd5e1c89 | ||
![]() |
04b7d9e2c3 | ||
![]() |
5f2743176a | ||
![]() |
3723488617 | ||
![]() |
0cc17a0791 | ||
![]() |
05a21e6091 | ||
![]() |
fc06bc7c6f | ||
![]() |
1bd005adc1 | ||
![]() |
4f208d1577 | ||
![]() |
eb6e0557a7 | ||
![]() |
cf09d66be6 | ||
![]() |
76cd127a8a | ||
![]() |
f6dd2a9968 | ||
![]() |
ae05a33e14 | ||
![]() |
b38fad323b | ||
![]() |
c6027fcf0f |
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
6
.github/workflows/closeYoutrackOnCommit.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
fetch-depth: 300
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-close-youtrack)" >> $GITHUB_ENV
|
||||
|
||||
- name: Update YouTrack
|
||||
run: ./gradlew updateYoutrackOnCommit
|
||||
run: ./gradlew --no-configuration-cache updateYoutrackOnCommit
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
||||
|
4
.github/workflows/integrationsTest.yml
vendored
4
.github/workflows/integrationsTest.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 300
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
4
.github/workflows/kover.yml
vendored
4
.github/workflows/kover.yml
vendored
@@ -18,10 +18,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 300
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
8
.github/workflows/mergePr.yml
vendored
8
.github/workflows/mergePr.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
if: github.event.pull_request.merged == true && github.repository == 'JetBrains/ideavim'
|
||||
if: false
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -20,17 +20,17 @@ jobs:
|
||||
fetch-depth: 50
|
||||
# See end of file updateChangeslog.yml for explanation of this secret
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Update authors
|
||||
id: update_authors
|
||||
run: ./gradlew updateMergedPr -PprId=${{ github.event.number }}
|
||||
run: ./gradlew --no-configuration-cache updateMergedPr -PprId=${{ github.event.number }}
|
||||
env:
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
12
.github/workflows/runUiOctopusTests.yml
vendored
12
.github/workflows/runUiOctopusTests.yml
vendored
@@ -9,20 +9,13 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Apply Patch
|
||||
run: |
|
||||
git apply tests/ui-ij-tests/src/test/kotlin/ui/octopus.patch
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -30,7 +23,7 @@ jobs:
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle runIdeForUiTests > build/reports/idea.log &
|
||||
gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -52,6 +45,7 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
|
7
.github/workflows/runUiPyTests.yml
vendored
7
.github/workflows/runUiPyTests.yml
vendored
@@ -18,11 +18,7 @@ jobs:
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Setup FFmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -52,4 +48,5 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-py-tests/build/reports
|
||||
sandbox-idea-log
|
7
.github/workflows/runUiTests.yml
vendored
7
.github/workflows/runUiTests.yml
vendored
@@ -15,11 +15,7 @@ jobs:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||
with:
|
||||
# Not strictly necessary, but it may prevent rate limit
|
||||
# errors especially on GitHub-hosted macos machines.
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
@@ -49,6 +45,7 @@ jobs:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-ij-tests/build/reports
|
||||
sandbox-idea-log
|
||||
# build-for-ui-test-linux:
|
||||
# runs-on: ubuntu-latest
|
||||
|
34
.github/workflows/updateAffectedRate.yml
vendored
34
.github/workflows/updateAffectedRate.yml
vendored
@@ -1,34 +0,0 @@
|
||||
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
|
||||
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle
|
||||
|
||||
# This workflow syncs changes from the docs folder of IdeaVim to the IdeaVim.wiki repository
|
||||
|
||||
name: Update Affected Rate field on YouTrack
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
|
||||
steps:
|
||||
- name: Fetch origin repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
||||
- name: Update YouTrack
|
||||
run: ./gradlew scripts:updateAffectedRates
|
||||
env:
|
||||
YOUTRACK_TOKEN: ${{ secrets.YOUTRACK_TOKEN }}
|
6
.github/workflows/updateAuthors.yml
vendored
6
.github/workflows/updateAuthors.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Update authors
|
||||
id: update_authors
|
||||
run: ./gradlew updateAuthors --stacktrace
|
||||
run: ./gradlew --no-configuration-cache updateAuthors --stacktrace
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
13
.github/workflows/updateChangelog.yml
vendored
13
.github/workflows/updateChangelog.yml
vendored
@@ -7,15 +7,12 @@ on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
# Workflow run on push is disabled to avoid conflicts when merging PR
|
||||
# push:
|
||||
# branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
if: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -25,10 +22,10 @@ jobs:
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Get tags
|
||||
run: git fetch --tags origin
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
@@ -39,7 +36,7 @@ jobs:
|
||||
echo "LAST_COMMIT=$(git rev-list -n 1 tags/workflow-changelog)" >> $GITHUB_ENV
|
||||
|
||||
- name: Update changelog
|
||||
run: ./gradlew updateChangelog
|
||||
run: ./gradlew --no-configuration-cache updateChangelog
|
||||
env:
|
||||
SUCCESS_COMMIT: ${{ env.LAST_COMMIT }}
|
||||
|
||||
@@ -63,4 +60,4 @@ jobs:
|
||||
# dependabot updates. See mergeDependatobPR.yml file.
|
||||
# However, it turned out that GitHub accepts pushes from the actions as a PR and requires checks, that are always
|
||||
# false for pushing from actions.
|
||||
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227
|
||||
# This secret is created to implement the workaround described in https://stackoverflow.com/a/76135647/3124227
|
||||
|
4
.github/workflows/updateFormatting.yml
vendored
4
.github/workflows/updateFormatting.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
fetch-depth: 50
|
||||
# See end of file updateChangeslog.yml for explanation of this secret
|
||||
ssh-key: ${{ secrets.PUSH_TO_PROTECTED_BRANCH_SECRET }}
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -10,6 +10,8 @@
|
||||
!/.idea/runConfigurations
|
||||
!/.idea/codeStyles
|
||||
!/.idea/vcs.xml
|
||||
!/.idea/misc.xml
|
||||
!/.idea/.name
|
||||
|
||||
**/build/
|
||||
**/out/
|
||||
@@ -22,7 +24,7 @@
|
||||
.teamcity/*.iml
|
||||
|
||||
# Generated by gradle task "generateGrammarSource"
|
||||
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||
vim-engine/src/main/java/com/maddyhome/idea/vim/parser/generated
|
||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||
# Generated JSONs for lazy classloading
|
||||
/vim-engine/src/main/resources/ksp-generated
|
||||
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
||||
IdeaVim
|
22
.idea/misc.xml
generated
Normal file
22
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<list size="3">
|
||||
<item index="0" class="java.lang.String" itemvalue="com.intellij.vim.annotations.CommandOrMotion" />
|
||||
<item index="1" class="java.lang.String" itemvalue="com.intellij.vim.annotations.ExCommand" />
|
||||
<item index="2" class="java.lang.String" itemvalue="com.intellij.vim.annotations.VimscriptFunction" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/.teamcity/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
25
.idea/runConfigurations/Start_IJ_with_IdeaVim__Split_Mode_.xml
generated
Normal file
25
.idea/runConfigurations/Start_IJ_with_IdeaVim__Split_Mode_.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Start IJ with IdeaVim (Split Mode)" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<log_file alias="idea.log" path="$PROJECT_DIR$/build/idea-sandbox/system/log/idea.log" />
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="runIdeSplitMode" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>false</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
@@ -11,6 +11,6 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
16
.teamcity/_Self/Constants.kt
vendored
16
.teamcity/_Self/Constants.kt
vendored
@@ -5,13 +5,13 @@ object Constants {
|
||||
const val EAP_CHANNEL = "eap"
|
||||
const val DEV_CHANNEL = "Dev"
|
||||
|
||||
const val GITHUB_TESTS = "2023.3.2"
|
||||
const val NVIM_TESTS = "2023.3.2"
|
||||
const val PROPERTY_TESTS = "2023.3.2"
|
||||
const val LONG_RUNNING_TESTS = "2023.3.2"
|
||||
const val QODANA_TESTS = "2023.3.2"
|
||||
const val RELEASE = "2023.3.2"
|
||||
const val GITHUB_TESTS = "2024.1.1"
|
||||
const val NVIM_TESTS = "2024.1.1"
|
||||
const val PROPERTY_TESTS = "2024.1.1"
|
||||
const val LONG_RUNNING_TESTS = "2024.1.1"
|
||||
const val QODANA_TESTS = "2024.1.1"
|
||||
const val RELEASE = "2024.1.1"
|
||||
|
||||
const val RELEASE_DEV = "2023.3.2"
|
||||
const val RELEASE_EAP = "2023.3.2"
|
||||
const val RELEASE_DEV = "2024.1.1"
|
||||
const val RELEASE_EAP = "2024.1.1"
|
||||
}
|
||||
|
4
.teamcity/_Self/Project.kt
vendored
4
.teamcity/_Self/Project.kt
vendored
@@ -11,6 +11,7 @@ import _Self.subprojects.GitHub
|
||||
import _Self.subprojects.OldTests
|
||||
import _Self.subprojects.Releases
|
||||
import _Self.vcsRoots.GitHubPullRequest
|
||||
import _Self.vcsRoots.ReleasesVcsRoot
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
||||
|
||||
@@ -21,10 +22,11 @@ object Project : Project({
|
||||
|
||||
// VCS roots
|
||||
vcsRoot(GitHubPullRequest)
|
||||
vcsRoot(ReleasesVcsRoot)
|
||||
|
||||
// Active tests
|
||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
||||
buildType(TestingBuildType("2024.1.1", "<default>"))
|
||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
|
||||
buildType(PropertyBased)
|
||||
|
72
.teamcity/_Self/buildTypes/CreateNewReleaseBranchFromMaster.kt
vendored
Normal file
72
.teamcity/_Self/buildTypes/CreateNewReleaseBranchFromMaster.kt
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package _Self.buildTypes
|
||||
|
||||
import _Self.IdeaVimBuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
||||
|
||||
object CreateNewReleaseBranchFromMaster : IdeaVimBuildType({
|
||||
name = "EXP: Create new release branch from master"
|
||||
|
||||
vcs {
|
||||
root(DslContext.settingsRoot)
|
||||
branchFilter = "+:<default>"
|
||||
|
||||
checkoutMode = CheckoutMode.AUTO
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
name = "Calculate next potential release version"
|
||||
scriptContent = """
|
||||
#!/bin/bash
|
||||
|
||||
# Fetch all remote branches
|
||||
git fetch --all
|
||||
|
||||
# Get a list of all branches matching the pattern releases/x.y.z
|
||||
branches=${'$'}(git branch -r | grep -oE 'releases/[0-9]+\.[0-9]+\.x')
|
||||
|
||||
# If no matching branches are found, print a message and exit
|
||||
if [[ -z "${'$'}branches" ]]; then
|
||||
echo "No release branches found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find the largest release version
|
||||
largest_release=${'$'}(echo "${'$'}branches" | sort -V | tail -n 1)
|
||||
|
||||
# Print the largest release
|
||||
echo "Largest release branch: ${'$'}largest_release"
|
||||
echo "##teamcity[setParameter name='env.POTENTIAL_VERSION' value='${'$'}largest_release']"
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
script {
|
||||
name = "Show potential release version"
|
||||
scriptContent = """
|
||||
#!/bin/bash
|
||||
echo "Calculated or user-provided parameter value is: %env.POTENTIAL_VERSION%"
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
params {
|
||||
param("env.POTENTIAL_VERSION", "")
|
||||
}
|
||||
|
||||
features {
|
||||
sshAgent {
|
||||
teamcitySshKey = "IdeaVim ssh keys"
|
||||
}
|
||||
}
|
||||
})
|
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
Normal file
42
.teamcity/_Self/buildTypes/PrintReleaseBranch.kt
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package _Self.buildTypes
|
||||
|
||||
import _Self.IdeaVimBuildType
|
||||
import _Self.vcsRoots.ReleasesVcsRoot
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
||||
|
||||
object PrintReleaseBranch : IdeaVimBuildType({
|
||||
name = "EXP: Print release branch"
|
||||
|
||||
vcs {
|
||||
root(ReleasesVcsRoot)
|
||||
branchFilter = "+:heads/releases/*"
|
||||
|
||||
checkoutMode = CheckoutMode.AUTO
|
||||
}
|
||||
|
||||
steps {
|
||||
|
||||
script {
|
||||
name = "Print current branch"
|
||||
scriptContent = """
|
||||
echo "Current branch is: %teamcity.build.branch%"
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
features {
|
||||
sshAgent {
|
||||
teamcitySshKey = "IdeaVim ssh keys"
|
||||
}
|
||||
}
|
||||
})
|
@@ -26,7 +26,7 @@ object PublishVimEngine : IdeaVimBuildType({
|
||||
|
||||
vcs {
|
||||
root(DslContext.settingsRoot)
|
||||
branchFilter = "+:<default>"
|
||||
branchFilter = "+:fleet"
|
||||
|
||||
checkoutMode = CheckoutMode.AUTO
|
||||
}
|
||||
|
3
.teamcity/_Self/buildTypes/Qodana.kt
vendored
3
.teamcity/_Self/buildTypes/Qodana.kt
vendored
@@ -46,8 +46,8 @@ object Qodana : IdeaVimBuildType({
|
||||
version = Qodana.JVMVersion.LATEST
|
||||
}
|
||||
reportAsTests = true
|
||||
additionalDockerArguments = "-e QODANA_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmdhbml6YXRpb24iOiIzUFZrQSIsInByb2plY3QiOiIzN1FlQSIsInRva2VuIjoiM0t2bXoifQ.uohp81tM7iAfvvB6k8faarfpV-OjusAaEbWQ8iNrOgs"
|
||||
additionalQodanaArguments = "--baseline qodana.sarif.json"
|
||||
cloudToken = "credentialsJSON:6b79412e-9198-4862-9223-c5019488f903"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ object Qodana : IdeaVimBuildType({
|
||||
timezone = "SERVER"
|
||||
}
|
||||
param("dayOfWeek", "Sunday")
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
|
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
Normal file
106
.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package _Self.buildTypes
|
||||
|
||||
import _Self.Constants.EAP_CHANNEL
|
||||
import _Self.Constants.RELEASE_EAP
|
||||
import _Self.IdeaVimBuildType
|
||||
import _Self.vcsRoots.ReleasesVcsRoot
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
||||
|
||||
object ReleaseEapFromBranch : IdeaVimBuildType({
|
||||
name = "EXP: Publish EAP Build from branch"
|
||||
description = "Build and publish EAP of IdeaVim plugin"
|
||||
|
||||
artifactRules = "build/distributions/*"
|
||||
|
||||
params {
|
||||
param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE_EAP)
|
||||
password(
|
||||
"env.ORG_GRADLE_PROJECT_publishToken",
|
||||
"credentialsJSON:61a36031-4da1-4226-a876-b8148bf32bde",
|
||||
label = "Password"
|
||||
)
|
||||
param("env.ORG_GRADLE_PROJECT_publishChannels", EAP_CHANNEL)
|
||||
password(
|
||||
"env.ORG_GRADLE_PROJECT_slackUrl",
|
||||
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
|
||||
label = "Slack Token"
|
||||
)
|
||||
password(
|
||||
"env.YOUTRACK_TOKEN",
|
||||
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
|
||||
display = ParameterDisplay.HIDDEN
|
||||
)
|
||||
}
|
||||
|
||||
vcs {
|
||||
root(ReleasesVcsRoot)
|
||||
branchFilter = """
|
||||
+:heads/(releases/*)
|
||||
""".trimIndent()
|
||||
|
||||
checkoutMode = CheckoutMode.AUTO
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
name = "Pull git tags"
|
||||
scriptContent = "git fetch --tags origin"
|
||||
}
|
||||
script {
|
||||
name = "Pull git history"
|
||||
scriptContent = "git fetch --unshallow"
|
||||
}
|
||||
gradle {
|
||||
name = "Calculate new eap version from branch"
|
||||
tasks = "scripts:calculateNewEapVersionFromBranch"
|
||||
}
|
||||
gradle {
|
||||
name = "Set TeamCity build number"
|
||||
tasks = "scripts:setTeamCityBuildNumber"
|
||||
}
|
||||
gradle {
|
||||
name = "Add release tag"
|
||||
tasks = "scripts:addReleaseTag"
|
||||
}
|
||||
gradle {
|
||||
name = "Publish plugin"
|
||||
tasks = "publishPlugin"
|
||||
}
|
||||
script {
|
||||
name = "Push changes to the repo"
|
||||
scriptContent = """
|
||||
branch=$(git branch --show-current)
|
||||
echo current branch is ${'$'}branch
|
||||
git push origin %build.number%
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
name = "YouTrack post release actions"
|
||||
tasks = "scripts:eapReleaseActions"
|
||||
}
|
||||
}
|
||||
|
||||
features {
|
||||
sshAgent {
|
||||
teamcitySshKey = "IdeaVim ssh keys"
|
||||
}
|
||||
}
|
||||
|
||||
failureConditions {
|
||||
failOnMetricChange {
|
||||
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
|
||||
threshold = 5
|
||||
units = BuildFailureOnMetric.MetricUnit.PERCENTS
|
||||
comparison = BuildFailureOnMetric.MetricComparison.DIFF
|
||||
compareTo = build {
|
||||
buildRule = lastSuccessful()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
77
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
77
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@@ -19,8 +19,6 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange
|
||||
|
||||
object ReleaseMajor : ReleasePlugin("major")
|
||||
object ReleaseMinor : ReleasePlugin("minor")
|
||||
@@ -97,14 +95,14 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Set TeamCity build number"
|
||||
tasks = "scripts:setTeamCityBuildNumber"
|
||||
}
|
||||
gradle {
|
||||
name = "Update change log"
|
||||
tasks = "scripts:changelogUpdateUnreleased"
|
||||
}
|
||||
gradle {
|
||||
name = "Commit preparation changes"
|
||||
tasks = "scripts:commitChanges"
|
||||
}
|
||||
// gradle {
|
||||
// name = "Update change log"
|
||||
// tasks = "scripts:changelogUpdateUnreleased"
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Commit preparation changes"
|
||||
// tasks = "scripts:commitChanges"
|
||||
// }
|
||||
gradle {
|
||||
name = "Add release tag"
|
||||
tasks = "scripts:addReleaseTag"
|
||||
@@ -117,33 +115,24 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Publish release"
|
||||
tasks = "publishPlugin"
|
||||
}
|
||||
script {
|
||||
name = "Checkout master branch"
|
||||
scriptContent = """
|
||||
echo Checkout master
|
||||
git checkout master
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
name = "Update change log in master"
|
||||
tasks = "scripts:changelogUpdateUnreleased"
|
||||
}
|
||||
gradle {
|
||||
name = "Commit preparation changes in master"
|
||||
tasks = "scripts:commitChanges"
|
||||
}
|
||||
// script {
|
||||
// name = "Checkout master branch"
|
||||
// scriptContent = """
|
||||
// echo Checkout master
|
||||
// git checkout master
|
||||
// """.trimIndent()
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Update change log in master"
|
||||
// tasks = "scripts:changelogUpdateUnreleased"
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Commit preparation changes in master"
|
||||
// tasks = "scripts:commitChanges"
|
||||
// }
|
||||
script {
|
||||
name = "Push changes to the repo"
|
||||
scriptContent = """
|
||||
branch=$(git branch --show-current)
|
||||
echo Current branch is ${'$'}branch
|
||||
if [ "master" != "${'$'}branch" ];
|
||||
then
|
||||
git checkout master
|
||||
fi
|
||||
|
||||
git push origin
|
||||
|
||||
git checkout release
|
||||
echo checkout release branch
|
||||
git branch --set-upstream-to=origin/release release
|
||||
@@ -156,10 +145,10 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Run Integrations"
|
||||
tasks = "releaseActions"
|
||||
}
|
||||
gradle {
|
||||
name = "Slack Notification"
|
||||
tasks = "slackNotification"
|
||||
}
|
||||
// gradle {
|
||||
// name = "Slack Notification"
|
||||
// tasks = "slackNotification"
|
||||
// }
|
||||
}
|
||||
|
||||
features {
|
||||
@@ -167,16 +156,4 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
teamcitySshKey = "IdeaVim ssh keys"
|
||||
}
|
||||
}
|
||||
|
||||
failureConditions {
|
||||
failOnMetricChange {
|
||||
metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE
|
||||
threshold = 5
|
||||
units = BuildFailureOnMetric.MetricUnit.PERCENTS
|
||||
comparison = BuildFailureOnMetric.MetricComparison.DIFF
|
||||
compareTo = build {
|
||||
buildRule = lastSuccessful()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
4
.teamcity/_Self/subprojects/GitHub.kt
vendored
4
.teamcity/_Self/subprojects/GitHub.kt
vendored
@@ -16,10 +16,10 @@ object GitHub : Project({
|
||||
name = "Pull Requests checks"
|
||||
description = "Automatic checking of GitHub Pull Requests"
|
||||
|
||||
buildType(Github("clean test", "Tests"))
|
||||
buildType(GithubBuildType("clean test", "Tests"))
|
||||
})
|
||||
|
||||
class Github(command: String, desc: String) : IdeaVimBuildType({
|
||||
class GithubBuildType(command: String, desc: String) : IdeaVimBuildType({
|
||||
name = "GitHub Pull Requests $desc"
|
||||
description = "Test GitHub pull requests $desc"
|
||||
|
||||
|
7
.teamcity/_Self/subprojects/Releases.kt
vendored
7
.teamcity/_Self/subprojects/Releases.kt
vendored
@@ -1,8 +1,11 @@
|
||||
package _Self.subprojects
|
||||
|
||||
import _Self.buildTypes.CreateNewReleaseBranchFromMaster
|
||||
import _Self.buildTypes.PrintReleaseBranch
|
||||
import _Self.buildTypes.PublishVimEngine
|
||||
import _Self.buildTypes.ReleaseDev
|
||||
import _Self.buildTypes.ReleaseEap
|
||||
import _Self.buildTypes.ReleaseEapFromBranch
|
||||
import _Self.buildTypes.ReleaseMajor
|
||||
import _Self.buildTypes.ReleaseMinor
|
||||
import _Self.buildTypes.ReleasePatch
|
||||
@@ -38,4 +41,8 @@ object Releases : Project({
|
||||
buildType(ReleaseEap)
|
||||
buildType(ReleaseDev)
|
||||
buildType(PublishVimEngine)
|
||||
|
||||
buildType(CreateNewReleaseBranchFromMaster)
|
||||
buildType(PrintReleaseBranch)
|
||||
buildType(ReleaseEapFromBranch)
|
||||
})
|
||||
|
13
.teamcity/_Self/vcsRoots/Releases.kt
vendored
Normal file
13
.teamcity/_Self/vcsRoots/Releases.kt
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package _Self.vcsRoots
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.vcs.GitVcsRoot
|
||||
|
||||
object ReleasesVcsRoot : GitVcsRoot({
|
||||
name = "IdeaVim Releases"
|
||||
url = "git@github.com:JetBrains/ideavim.git"
|
||||
branch = "refs/heads/master"
|
||||
branchSpec = "+:refs/(*)"
|
||||
authMethod = uploadedKey {
|
||||
uploadedKey = "IdeaVim ssh keys"
|
||||
}
|
||||
})
|
@@ -1,11 +1,9 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.RelativeId
|
||||
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.changeBuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.expectSteps
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.update
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
@@ -13,6 +11,18 @@ 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"
|
||||
|
@@ -5,15 +5,15 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
To apply the patch, change the buildType with id = 'PublishVimEngine'
|
||||
To apply the patch, change the buildType with id = 'ReleaseEapFromBranch'
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeBuildType(RelativeId("PublishVimEngine")) {
|
||||
changeBuildType(RelativeId("ReleaseEapFromBranch")) {
|
||||
vcs {
|
||||
|
||||
check(branchFilter == "+:<default>") {
|
||||
check(branchFilter == "+:heads/(releases/*)") {
|
||||
"Unexpected option value: branchFilter = $branchFilter"
|
||||
}
|
||||
branchFilter = "+:fleet"
|
||||
branchFilter = "heads/releases/*"
|
||||
}
|
||||
}
|
2
.teamcity/settings.kts
vendored
2
.teamcity/settings.kts
vendored
@@ -30,5 +30,5 @@ node (Plugins -> teamcity-configs -> teamcity-configs:generate),
|
||||
the 'Debug' option is available in the context menu for the task.
|
||||
*/
|
||||
|
||||
version = "2023.11"
|
||||
version = "2024.03"
|
||||
project(_Self.Project)
|
||||
|
28
AUTHORS.md
28
AUTHORS.md
@@ -495,6 +495,34 @@ Contributors:
|
||||
[![icon][github]](https://github.com/emanuelgestosa)
|
||||
|
||||
Emanuel Gestosa
|
||||
* [![icon][mail]](mailto:81118900+lippfi@users.noreply.github.com)
|
||||
[![icon][github]](https://github.com/lippfi)
|
||||
|
||||
lippfi,
|
||||
* [![icon][mail]](mailto:fillipser143@gmail.com)
|
||||
[![icon][github]](https://github.com/Parker7123)
|
||||
|
||||
FilipParker
|
||||
* [![icon][mail]](mailto:7138209+duhaesbaert@users.noreply.github.com)
|
||||
[![icon][github]](https://github.com/duhaesbaert)
|
||||
|
||||
Eduardo Haesbaert
|
||||
* [![icon][mail]](mailto:nikolaevsky.egor@gmail.com)
|
||||
[![icon][github]](https://github.com/Aisper)
|
||||
|
||||
Egor Nikolaevsky
|
||||
* [![icon][mail]](mailto:77796630+throwaway69420-69420@users.noreply.github.com)
|
||||
[![icon][github]](https://github.com/kun-codes)
|
||||
|
||||
Bishwa Saha,
|
||||
* [![icon][mail]](mailto:alexfu@fastmail.com)
|
||||
[![icon][github]](https://github.com/alexfu)
|
||||
|
||||
Alex Fu
|
||||
* [![icon][mail]](mailto:jakepeters199@hotmail.com)
|
||||
[![icon][github]](https://github.com/LazyScaper)
|
||||
|
||||
Jake
|
||||
|
||||
Previous contributors:
|
||||
|
||||
|
12
CHANGES.md
12
CHANGES.md
@@ -23,15 +23,19 @@ It is important to distinguish EAP from traditional pre-release software.
|
||||
Please note that the quality of EAP versions may at times be way below even
|
||||
usual beta standards.
|
||||
|
||||
## To Be Released
|
||||
## End of changelog file maintenance
|
||||
|
||||
Since version 2.9.0, the changelog can be found on YouTrack
|
||||
|
||||
To Be Released: https://youtrack.jetbrains.com/issues/VIM?q=%23%7BReady%20To%20Release%7D%20
|
||||
Latest Fixes: https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20sort%20by:%20updated%20
|
||||
|
||||
## 2.9.0, 2024-02-20
|
||||
|
||||
### Fixes:
|
||||
* [VIM-3055](https://youtrack.jetbrains.com/issue/VIM-3055) Fix the issue with double deleting after dot
|
||||
* [VIM-3291](https://youtrack.jetbrains.com/issue/VIM-3291) Remove sync of editor selection between different opened editors
|
||||
* [VIM-3234](https://youtrack.jetbrains.com/issue/VIM-3234) The space character won't mix in the tab chars after >> and << commands
|
||||
|
||||
### Merged PRs:
|
||||
* [725](https://github.com/JetBrains/ideavim/pull/725) by [Emanuel Gestosa](https://github.com/emanuelgestosa): Regex
|
||||
* [805](https://github.com/JetBrains/ideavim/pull/805) by [chylex](https://github.com/chylex): VIM-3238 Fix recording a macro that replays another macro
|
||||
|
||||
## 2.8.0, 2024-01-30
|
||||
|
@@ -1,6 +1,5 @@
|
||||
IdeaVim project is licensed under MIT license except the following parts of it:
|
||||
|
||||
* File [RegExp.kt](src/main/java/com/maddyhome/idea/vim/regexp/RegExp.kt) is licensed under Vim License.
|
||||
* File [ScrollViewHelper.kt](com/maddyhome/idea/vim/helper/ScrollViewHelper.kt) is licensed under Vim License.
|
||||
* File [Tutor.kt](src/main/java/com/maddyhome/idea/vim/ui/Tutor.kt) is licensed under Vim License.
|
||||
|
||||
|
@@ -21,7 +21,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.17")
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:2.0.0-1.0.22")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:$kotlinxSerializationVersion") {
|
||||
// kotlin stdlib is provided by IJ, so there is no need to include it into the distribution
|
||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||
|
@@ -13,7 +13,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||
import com.intellij.vim.processors.VimscriptFunctionProcessor
|
||||
|
||||
public class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
||||
class VimscriptFunctionProcessorProvider : SymbolProcessorProvider {
|
||||
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
|
||||
return VimscriptFunctionProcessor(environment)
|
||||
}
|
||||
|
196
build.gradle.kts
196
build.gradle.kts
@@ -32,7 +32,6 @@ import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.RepositoryBuilder
|
||||
import org.intellij.markdown.ast.getTextInNode
|
||||
import org.jetbrains.changelog.Changelog
|
||||
import org.jetbrains.changelog.exceptions.MissingVersionException
|
||||
import org.kohsuke.github.GHUser
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
@@ -49,14 +48,14 @@ buildscript {
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
||||
classpath("org.kohsuke:github-api:1.305")
|
||||
|
||||
classpath("io.ktor:ktor-client-core:2.3.7")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.7")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.7")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.7")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
|
||||
classpath("io.ktor:ktor-client-core:2.3.12")
|
||||
classpath("io.ktor:ktor-client-cio:2.3.10")
|
||||
classpath("io.ktor:ktor-client-auth:2.3.12")
|
||||
classpath("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
classpath("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
||||
|
||||
// This comes from the changelog plugin
|
||||
// classpath("org.jetbrains:markdown:0.3.1")
|
||||
@@ -64,36 +63,20 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
antlr
|
||||
java
|
||||
kotlin("jvm") version "1.9.22"
|
||||
application
|
||||
id("java-test-fixtures")
|
||||
|
||||
id("org.jetbrains.intellij") version "1.17.1"
|
||||
id("org.jetbrains.intellij") version "1.17.3"
|
||||
id("org.jetbrains.changelog") version "2.2.0"
|
||||
|
||||
id("org.jetbrains.kotlinx.kover") version "0.6.1"
|
||||
id("com.dorongold.task-tree") version "2.1.1"
|
||||
id("com.dorongold.task-tree") version "4.0.0"
|
||||
|
||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
||||
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
||||
arg("ex_commands_file", "intellij_ex_commands.json")
|
||||
arg("commands_file", "intellij_commands.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
||||
tasks.named("kspKotlin").configure { dependsOn("generateGrammarSource") }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestKotlin").configure { enabled = false }
|
||||
}
|
||||
|
||||
// Import variables from gradle.properties file
|
||||
val javaVersion: String by project
|
||||
val kotlinVersion: String by project
|
||||
@@ -101,8 +84,8 @@ val ideaVersion: String by project
|
||||
val ideaType: String by project
|
||||
val downloadIdeaSources: String by project
|
||||
val instrumentPluginCode: String by project
|
||||
val antlrVersion: String by project
|
||||
val remoteRobotVersion: String by project
|
||||
val splitModeVersion: String by project
|
||||
|
||||
val publishChannels: String by project
|
||||
val publishToken: String by project
|
||||
@@ -110,6 +93,8 @@ val publishToken: String by project
|
||||
val slackUrl: String by project
|
||||
val youtrackToken: String by project
|
||||
|
||||
val releaseType: String? by project
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = uri("https://cache-redirector.jetbrains.com/intellij-dependencies") }
|
||||
@@ -118,12 +103,10 @@ repositories {
|
||||
dependencies {
|
||||
api(project(":vim-engine"))
|
||||
ksp(project(":annotation-processors"))
|
||||
implementation(project(":annotation-processors"))
|
||||
compileOnly(project(":annotation-processors"))
|
||||
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
||||
compileOnly("org.jetbrains:annotations:24.1.0")
|
||||
runtimeOnly("org.antlr:antlr4-runtime:$antlrVersion")
|
||||
antlr("org.antlr:antlr4:$antlrVersion")
|
||||
|
||||
// --------- Test dependencies ----------
|
||||
|
||||
@@ -142,14 +125,14 @@ dependencies {
|
||||
testFixturesImplementation("org.jetbrains.kotlin:kotlin-test:$kotlinVersion")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.mockito.kotlin/mockito-kotlin
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
|
||||
testImplementation("org.mockito.kotlin:mockito-kotlin:5.3.1")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.3")
|
||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-params:5.10.3")
|
||||
}
|
||||
|
||||
configurations {
|
||||
@@ -160,6 +143,8 @@ configurations {
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
// Set teamcity env variable locally to run additional tests for leaks.
|
||||
// By default, this test runs on TC only, but this test doesn't take a lot of time,
|
||||
// so we can turn it on for local development
|
||||
@@ -172,6 +157,9 @@ tasks {
|
||||
}
|
||||
|
||||
compileJava {
|
||||
// CodeQL can't resolve the 'by project' property, so we need to give it a hint. This is the minimum version we need
|
||||
// so doesn't have to match exactly
|
||||
// Hint for the CodeQL autobuilder: sourceCompatibility = 17
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
|
||||
@@ -197,6 +185,10 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
runIde {
|
||||
systemProperty("octopus.handler", System.getProperty("octopus.handler") ?: true)
|
||||
}
|
||||
|
||||
downloadRobotServerPlugin {
|
||||
version.set(remoteRobotVersion)
|
||||
}
|
||||
@@ -207,6 +199,33 @@ tasks {
|
||||
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)
|
||||
}
|
||||
|
||||
// Add plugin open API sources to the plugin ZIP
|
||||
val createOpenApiSourceJar by registering(Jar::class) {
|
||||
// Java sources
|
||||
from(sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
}
|
||||
from(project(":vim-engine").sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
}
|
||||
// Kotlin sources
|
||||
from(kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||
archiveClassifier.set("src")
|
||||
}
|
||||
|
||||
buildPlugin {
|
||||
dependsOn(createOpenApiSourceJar)
|
||||
from(createOpenApiSourceJar) { into("lib/src") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +236,6 @@ java {
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(javaVersion))
|
||||
}
|
||||
@@ -260,81 +278,33 @@ tasks {
|
||||
runPluginVerifier {
|
||||
downloadDir.set("${project.buildDir}/pluginVerifier/ides")
|
||||
teamCityOutputFormat.set(true)
|
||||
// ideVersions.set(listOf("IC-2021.3.4"))
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
maxHeapSize = "128m"
|
||||
arguments.addAll(listOf("-package", "com.maddyhome.idea.vim.vimscript.parser.generated", "-visitor"))
|
||||
outputDirectory = file("src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated")
|
||||
}
|
||||
|
||||
named("compileKotlin") {
|
||||
dependsOn("generateGrammarSource")
|
||||
}
|
||||
named("compileTestKotlin") {
|
||||
dependsOn("generateTestGrammarSource")
|
||||
}
|
||||
named("compileTestFixturesKotlin") {
|
||||
dependsOn("generateTestFixturesGrammarSource")
|
||||
}
|
||||
|
||||
// Add plugin open API sources to the plugin ZIP
|
||||
val createOpenApiSourceJar by registering(Jar::class) {
|
||||
dependsOn("generateGrammarSource")
|
||||
// Java sources
|
||||
from(sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
}
|
||||
from(project(":vim-engine").sourceSets.main.get().java) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.java")
|
||||
}
|
||||
// Kotlin sources
|
||||
from(kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
from(project(":vim-engine").kotlin.sourceSets.main.get().kotlin) {
|
||||
include("**/com/maddyhome/idea/vim/**/*.kt")
|
||||
}
|
||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||
archiveClassifier.set("src")
|
||||
}
|
||||
|
||||
buildPlugin {
|
||||
dependsOn(createOpenApiSourceJar)
|
||||
from(createOpenApiSourceJar) { into("lib/src") }
|
||||
}
|
||||
|
||||
val pluginVersion = version
|
||||
patchPluginXml {
|
||||
// Don't forget to update plugin.xml
|
||||
patchPluginXml {
|
||||
// Get the latest available change notes from the changelog file
|
||||
changeNotes.set(
|
||||
provider {
|
||||
with(changelog) {
|
||||
val log = try {
|
||||
getUnreleased()
|
||||
} catch (e: MissingVersionException) {
|
||||
getOrNull(pluginVersion.toString()) ?: getLatest()
|
||||
}
|
||||
renderItem(
|
||||
log,
|
||||
org.jetbrains.changelog.Changelog.OutputType.HTML,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
sinceBuild.set("241.15989.150")
|
||||
|
||||
// --- Tests
|
||||
|
||||
tasks {
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
changeNotes.set(
|
||||
"""<a href="https://youtrack.jetbrains.com/issues/VIM?q=State:%20Fixed%20Fix%20versions:%20${version.get()}">Changelog</a>"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("generated_directory", "$projectDir/src/main/resources/ksp-generated")
|
||||
arg("vimscript_functions_file", "intellij_vimscript_functions.json")
|
||||
arg("ex_commands_file", "intellij_ex_commands.json")
|
||||
arg("commands_file", "intellij_commands.json")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// tasks.named("kspKotlin").configure { dependsOn("clean") }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestFixturesKotlin").configure { enabled = false }
|
||||
tasks.named("kspTestKotlin").configure { enabled = false }
|
||||
}
|
||||
|
||||
|
||||
// --- Changelog
|
||||
|
||||
changelog {
|
||||
@@ -429,12 +399,14 @@ val prId: String by project
|
||||
|
||||
tasks.register("updateMergedPr") {
|
||||
doLast {
|
||||
if (project.hasProperty("prId")) {
|
||||
println("Got pr id: $prId")
|
||||
updateMergedPr(prId.toInt())
|
||||
} else {
|
||||
error("Cannot get prId")
|
||||
}
|
||||
val x = changelog.getUnreleased()
|
||||
println("x")
|
||||
// if (project.hasProperty("prId")) {
|
||||
// println("Got pr id: $prId")
|
||||
// updateMergedPr(prId.toInt())
|
||||
// } else {
|
||||
// error("Cannot get prId")
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,6 +430,8 @@ val fixVersionsElementType = "VersionBundleElement"
|
||||
tasks.register("releaseActions") {
|
||||
group = "other"
|
||||
doLast {
|
||||
if (releaseType == "patch") return@doLast
|
||||
|
||||
val tickets = getYoutrackTicketsByQuery("%23%7BReady+To+Release%7D%20and%20tag:%20%7BIdeaVim%20Released%20In%20EAP%7D%20")
|
||||
if (tickets.isNotEmpty()) {
|
||||
println("Updating statuses for tickets: $tickets")
|
||||
|
@@ -129,8 +129,26 @@ Original plugin: [vim-multiple-cursors](https://github.com/terryma/vim-multiple-
|
||||
</details>
|
||||
|
||||
### Instructions
|
||||
|
||||
https://github.com/terryma/vim-multiple-cursors/blob/master/doc/multiple_cursors.txt
|
||||
|
||||
At the moment, the default key binds for this plugin do not get mapped correctly in IdeaVim (see [VIM-2178](https://youtrack.jetbrains.com/issue/VIM-2178)). To enable the default key binds, add the following to your `.ideavimrc` file...
|
||||
|
||||
```
|
||||
" Remap multiple-cursors shortcuts to match terryma/vim-multiple-cursors
|
||||
nmap <C-n> <Plug>NextWholeOccurrence
|
||||
xmap <C-n> <Plug>NextWholeOccurrence
|
||||
nmap g<C-n> <Plug>NextOccurrence
|
||||
xmap g<C-n> <Plug>NextOccurrence
|
||||
xmap <C-x> <Plug>SkipOccurrence
|
||||
xmap <C-p> <Plug>RemoveOccurrence
|
||||
|
||||
" Note that the default <A-n> and g<A-n> shortcuts don't work on Mac due to dead keys.
|
||||
" <A-n> is used to enter accented text e.g. ñ
|
||||
" Feel free to pick your own mappings that are not affected. I like to use <leader>
|
||||
nmap <leader><C-n> <Plug>AllWholeOccurrences
|
||||
xmap <leader><C-n> <Plug>AllWholeOccurrences
|
||||
nmap <leader>g<C-n> <Plug>AllOccurrences
|
||||
xmap <leader>g<C-n> <Plug>AllOccurrences
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
@@ -27,7 +27,7 @@ Plug 'nerdtree'
|
||||
### Preview
|
||||
|
||||
<details>
|
||||
<summary>Click to the the preview</summary>
|
||||
<summary>Click for the preview</summary>
|
||||
<img src="images/nerdtree.gif" alt="NERDTree example"/>
|
||||
</details>
|
||||
|
||||
@@ -40,30 +40,33 @@ Plug 'nerdtree'
|
||||
- `:NERDTreeFind`
|
||||
- `:NERDTreeRefreshRoot`
|
||||
|
||||
| Key | Description | Map Setting |
|
||||
|---------|---------------------------------------------------------|--------------------------------|
|
||||
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
||||
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
||||
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
||||
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
||||
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
||||
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
||||
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
||||
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
||||
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
||||
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
||||
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
||||
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
||||
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
||||
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
||||
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
||||
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
||||
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
||||
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
||||
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
||||
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
||||
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
||||
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
||||
| Key | Description | Map Setting |
|
||||
|---------|--------------------------------------------------------|--------------------------------|
|
||||
| `o` | Open files, directories and bookmarks | `g:NERDTreeMapActivateNode` |
|
||||
| `go` | Open selected file, but leave cursor in the NERDTree | `g:NERDTreeMapPreview` |
|
||||
| `t` | Open selected node/bookmark in a new tab | `g:NERDTreeMapOpenInTab` |
|
||||
| `T` | Same as 't' but keep the focus on the current tab | `g:NERDTreeMapOpenInTabSilent` |
|
||||
| `i` | Open selected file in a split window | `g:NERDTreeMapOpenSplit` |
|
||||
| `gi` | Same as i, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewSplit` |
|
||||
| `s` | Open selected file in a new vsplit | `g:NERDTreeMapOpenVSplit` |
|
||||
| `gs` | Same as s, but leave the cursor on the NERDTree | `g:NERDTreeMapPreviewVSplit` |
|
||||
| `O` | Recursively open the selected directory | `g:NERDTreeMapOpenRecursively` |
|
||||
| `x` | Close the current nodes parent | `g:NERDTreeMapCloseDir` |
|
||||
| `X` | Recursively close all children of the current node | `g:NERDTreeMapCloseChildren` |
|
||||
| `P` | Jump to the root node | `g:NERDTreeMapJumpRoot` |
|
||||
| `p` | Jump to current nodes parent | `g:NERDTreeMapJumpParent` |
|
||||
| `K` | Jump up inside directories at the current tree depth | `g:NERDTreeMapJumpFirstChild` |
|
||||
| `J` | Jump down inside directories at the current tree depth | `g:NERDTreeMapJumpLastChild` |
|
||||
| `<C-J>` | Jump down to next sibling of the current directory | `g:NERDTreeMapJumpNextSibling` |
|
||||
| `<C-K>` | Jump up to previous sibling of the current directory | `g:NERDTreeMapJumpPrevSibling` |
|
||||
| `r` | Recursively refresh the current directory | `g:NERDTreeMapRefresh` |
|
||||
| `R` | Recursively refresh the current root | `g:NERDTreeMapRefreshRoot` |
|
||||
| `m` | Display the NERDTree menu | `g:NERDTreeMapMenu` |
|
||||
| `q` | Close the NERDTree window | `g:NERDTreeMapQuit` |
|
||||
| `A` | Zoom (maximize/minimize) the NERDTree window | `g:NERDTreeMapToggleZoom` |
|
||||
| `d` | Delete file or directory | `g:NERDTreeMapDelete` |
|
||||
| `n` | Create File | `g:NERDTreeMapNewFile` |
|
||||
| `N` | Create Directory | `g:NERDTreeMapNewDir` |
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
|
@@ -1,140 +1,268 @@
|
||||
List of Supported Set Commands
|
||||
==============================
|
||||
List of Supported Options
|
||||
=========================
|
||||
|
||||
The following `:set` commands can appear in `~/.ideavimrc` or be set manually in the command mode:
|
||||
The following options can be set with the `:set`, `:setglobal` and `:setlocal` commands.
|
||||
They can be added to the `~/.ideavimrc` file, or set manually in Command-line mode.
|
||||
For more details of each option, please see the Vim documentation.
|
||||
Every effort is made to make these options compatible with Vim behaviour.
|
||||
However, some differences are inevitable.
|
||||
|
||||
'clipboard' 'cb' clipboard options
|
||||
Standard clipboard options plus
|
||||
```
|
||||
'clipboard' 'cb' Defines clipboard behavioue
|
||||
A comma-separated list of words to control clipboard behaviour:
|
||||
unnamed The clipboard register '*' is used instead of the
|
||||
unnamed register
|
||||
unnamedplus The clipboard register '+' is used instead of the
|
||||
unnamed register
|
||||
ideaput Uses the IDEs own paste implementation for put
|
||||
operations rather than simply inserting the text
|
||||
|
||||
'digraph' 'dg' Enable using <BS> to enter digraphs in Insert mode
|
||||
'gdefault' 'gd' The ":substitute" flag 'g' is by default
|
||||
'guicursor' 'gcr' Controls the shape of the cursor for different modes
|
||||
'history' 'hi' Number of command-lines that are remembered
|
||||
'hlsearch' 'hls' Highlight matches with the last search pattern
|
||||
'ignorecase' 'ic' Ignore case in search patterns
|
||||
'incsearch' 'is' Show where search pattern typed so far matches
|
||||
'iskeyword' 'isk' Defines keywords for commands like 'w', '*', etc.
|
||||
'keymodel' 'km' Controls selection behaviour with special keys
|
||||
List of comma separated words, which enable special things that keys
|
||||
can do. These values can be used:
|
||||
startsel Using a shifted special key starts selection (either
|
||||
Select mode or Visual mode, depending on "key" being
|
||||
present in 'selectmode')
|
||||
stopsel Using a NOT-shifted special key stops selection.
|
||||
Automatically enables `stopselect` and `stopvisual`
|
||||
stopselect Using a NOT-shifted special key stops select mode
|
||||
and removes selection - IdeaVim ONLY
|
||||
stopvisual Using a NOT-shifted special key stops visual mode
|
||||
and removes selection - IdeaVim ONLY
|
||||
continueselect Using a shifted arrow key doesn't start selection,
|
||||
but in select mode acts like startsel is enabled
|
||||
- IdeaVim ONLY
|
||||
continuevisual Using a shifted arrow key doesn't start selection,
|
||||
but in visual mode acts like startsel is enabled
|
||||
- IdeaVim ONLY
|
||||
|
||||
`ideaput` (default on) - IdeaVim ONLY
|
||||
enable native idea paste action for put operations
|
||||
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
'gdefault' 'gd' the ":substitute" flag 'g' is by default
|
||||
'history' 'hi' number of command-lines that are remembered
|
||||
'hlsearch' 'hls' highlight matches with the last search pattern
|
||||
'ignorecase' 'ic' ignore case in search patterns
|
||||
'iskeyword' 'isk' defines keywords for commands like 'w', '*', etc.
|
||||
'incsearch' 'is' show where search pattern typed so far matches
|
||||
|
||||
`keymodel` `km` String (default "continueselect,stopselect")
|
||||
Special keys in this context are the cursor keys, <End>, <Home>,
|
||||
<PageUp> and <PageDown>.
|
||||
|
||||
List of comma separated words, which enable special things that keys
|
||||
can do. These values can be used:
|
||||
startsel Using a shifted special[1] key starts selection (either
|
||||
Select mode or Visual mode, depending on "key" being
|
||||
present in 'selectmode').
|
||||
stopsel Using a NOT-shifted special[1] key stops selection.
|
||||
Automatically enables `stopselect` and `stopvisual`
|
||||
stopselect Using a NOT-shifted special[1] key stops - IdeaVim ONLY
|
||||
select mode and removes selection.
|
||||
stopvisual Using a NOT-shifted special[1] key stops - IdeaVim ONLY
|
||||
visual mode and removes selection.
|
||||
continueselect Using a shifted arrow key doesn't - IdeaVim ONLY
|
||||
start selection, but in select mode
|
||||
acts like startsel is enabled
|
||||
continuevisual Using a shifted arrow key doesn't - IdeaVim ONLY
|
||||
start selection, but in visual mode
|
||||
acts like startsel is enabled
|
||||
|
||||
'matchpairs' 'mps' pairs of characters that "%" can match
|
||||
'maxmapdepth' 'mmd' Maximum depth of mappings
|
||||
'more' 'more' When on, listings pause when the whole screen is filled.
|
||||
'nrformats' 'nf' number formats recognized for CTRL-A command
|
||||
'number' 'nu' print the line number in front of each line
|
||||
'relativenumber' 'rnu' show the line number relative to the line with
|
||||
the cursor
|
||||
'scroll' 'scr' lines to scroll with CTRL-U and CTRL-D
|
||||
'scrolljump' 'sj' minimum number of lines to scroll
|
||||
'scrolloff' 'so' minimum number of lines above and below the cursor
|
||||
'selection' 'sel' what type of selection to use
|
||||
'matchpairs' 'mps' Pairs of characters that "%" can match
|
||||
'maxmapdepth' 'mmd' Maximum depth of mappings
|
||||
'more' 'more' When on, listings pause when the whole screen is filled
|
||||
'nrformats' 'nf' Number formats recognized for CTRL-A command
|
||||
'operatorfunc' 'opfunc' Name of a function to call with the g@ operator
|
||||
'scroll' 'scr' Number of lines to scroll with CTRL-U and CTRL-D
|
||||
'selection' 'sel' What type of selection to use
|
||||
'selectmode' 'slm' Controls when to start Select mode instead of Visual
|
||||
This is a comma-separated list of words:
|
||||
|
||||
mouse When using the mouse
|
||||
key When using shifted special[1] keys
|
||||
cmd When using "v", "V", or <C-V>
|
||||
ideaselection When IDE sets a selection - IdeaVim ONLY
|
||||
(e.g.: extend selection, wrap with while, etc.)
|
||||
|
||||
`selectmode` `slm` String (default "")
|
||||
'shell' 'sh' The shell to use to execute commands with ! and :!
|
||||
'shellcmdflag' 'shcf' The command flag passed to the shell
|
||||
'shellxescape' 'sxe' The characters to be escaped when calling a shell
|
||||
'shellxquote' 'sxq' The quote character to use in a shell command
|
||||
'showcmd' 'sc' Show (partial) command in the status bar
|
||||
'showmode' 'smd' Show the current mode in the status bar
|
||||
'smartcase' 'scs' Use case sensitive search if any character in the
|
||||
pattern is uppercase
|
||||
'startofline' 'sol' When on, some commands move the cursor to the first
|
||||
non-blank of the line
|
||||
When off, the cursor is kept in the same column
|
||||
(if possible)
|
||||
'timeout' 'to' Use timeout for mapped key sequences
|
||||
'timeoutlen' 'tm' Timeout duration for a mapped key sequence
|
||||
'viminfo' 'vi' Information to remember after restart
|
||||
'virtualedit' 've' Placement of the cursor where there is no actual text
|
||||
A comma-separated list of these words:
|
||||
block Allow virtual editing in Visual mode (not supported)
|
||||
insert Allow virtual editing in Insert mode (not supported)
|
||||
all Allow virtual editing in all modes (not supported)
|
||||
onemore Allow the cursor to move just past the end of the line
|
||||
|
||||
This is a comma-separated list of words, which specify when to start
|
||||
Select mode instead of Visual mode, when a selection is started.
|
||||
Possible values:
|
||||
mouse when using the mouse
|
||||
key when using shifted special[1] keys
|
||||
cmd when using "v", "V", or <C-V>
|
||||
ideaselection when IDE sets a selection - IdeaVim ONLY
|
||||
(examples: extend selection, wrap with while, etc.)
|
||||
'visualbell' 'vb' When on, prevents beeping on error
|
||||
'whichwrap' 'ww' Which keys that move the cursor left/right can wrap to
|
||||
other lines
|
||||
A comma-separated list of these flags:
|
||||
char key modes
|
||||
b <BS> Normal and Visual
|
||||
s <Space> Normal and Visual
|
||||
h "h" Normal and Visual
|
||||
l "l" Normal and Visual
|
||||
< <Left> Normal and Visual
|
||||
> <Right> Normal and Visual
|
||||
~ "~" Normal
|
||||
[ <Left> Insert and Replace
|
||||
] <Right> Insert and Replace
|
||||
|
||||
`startofline` `sol` When "on" some commands move the cursor to the first non-blank of the line.
|
||||
When off the cursor is kept in the same column (if possible).
|
||||
'wrapscan' 'ws' Search will wrap around the end of file
|
||||
```
|
||||
|
||||
'showmode' 'smd' message on the status line to show current mode
|
||||
'showcmd' 'sc' show (partial) command in the status bar
|
||||
'sidescroll' 'ss' minimum number of columns to scroll horizontally
|
||||
'sidescrolloff' 'siso' min. number of columns to left and right of cursor
|
||||
'smartcase' 'scs' no ignore case when pattern is uppercase
|
||||
'timeout' 'to' use timeout for mapped key sequences
|
||||
'timeoutlen' 'tm' timeout duration for a mapped key sequence
|
||||
'undolevels' 'ul' maximum number of changes that can be undone
|
||||
'viminfo' 'vi' information to remember after restart
|
||||
'visualbell' 'vb' use visual bell instead of beeping
|
||||
'wrapscan' 'ws' searches wrap around the end of file
|
||||
|
||||
|
||||
|
||||
IdeaVim only commands:
|
||||
## IdeaVim options mapped to IntelliJ-based IDE settings
|
||||
|
||||
`ideamarks` `ideamarks` Boolean (default true)
|
||||
|
||||
If true, creation of global mark will trigger creation of IDE's bookmark
|
||||
and vice versa.
|
||||
|
||||
`idearefactormode` `idearefactormode` String(default "select")
|
||||
|
||||
Define the mode that would be enabled during
|
||||
the refactoring (renaming, live template, introduce variable, etc)
|
||||
|
||||
Use one of the following values:
|
||||
- keep - keep the mode that was enabled before starting a refactoring
|
||||
- select - start refactoring in select mode
|
||||
- visual - start refactoring in visual mode
|
||||
|
||||
This option has effect if you are in normal, insert or replace mode before refactoring start.
|
||||
Visual or select mode are not changed.
|
||||
|
||||
|
||||
`ideajoin` `ideajoin` Boolean (default false)
|
||||
|
||||
If true, join command will be performed via IDE
|
||||
See wiki/`ideajoin` examples
|
||||
|
||||
`ideastatusicon` `ideastatusicon` String(default "enabled")
|
||||
|
||||
Define the behavior of IdeaVim icon in the status bar.
|
||||
|
||||
Use one of the following values:
|
||||
- enabled - icon is shown in the status bar
|
||||
- gray - use the gray version of the icon
|
||||
- disabled - hide the icon
|
||||
IdeaVim provides its own implementation for handling scroll jump and offset, even though IntelliJ-based IDEs have similar functionality (there are differences in behaviour).
|
||||
When IdeaVim is hosted in an IntelliJ-based IDE (but not JetBrains Fleet), the following options map to the equivalent IDE settings:
|
||||
|
||||
`ideawrite` `ideawrite` String (default "all")
|
||||
"file" or "all". Defines the behaviour of ":w" command.
|
||||
Value "all" enables execution of ":wa" (save all) command on ":w" (save).
|
||||
This feature exists because some IJ options like "Prettier on save" or "ESlint on save"
|
||||
work only with "save all" action. If this option is set to "all", these actions work
|
||||
also with ":w" command.
|
||||
|
||||
`lookupkeys` `lookupkeys` List of strings
|
||||
|
||||
List of keys that should be processed by the IDE during the active lookup (autocompletion).
|
||||
For example, <Tab> and <Enter> are used by the IDE to finish the lookup,
|
||||
but <C-W> should be passed to IdeaVim.
|
||||
Default value:
|
||||
"<Tab>", "<Down>", "<Up>", "<Enter>", "<Left>", "<Right>",
|
||||
"<C-Down>", "<C-Up>", "<PageUp>", "<PageDown>",
|
||||
"<C-J>", "<C-Q>"
|
||||
|
||||
`ideavimsupport` `ideavimsupport` List of strings (default "dialog")
|
||||
|
||||
Define the list of additional buffers where IdeaVim is enabled.
|
||||
|
||||
- dialog - enable IdeaVim in dialogs
|
||||
- singleline - enable IdeaVim in single line editors (not suggested)
|
||||
```
|
||||
'scrolljump' 'sj' Minimal number of lines to scroll
|
||||
'scrolloff' 'so' Minimal number of lines above and below the cursor
|
||||
'sidescroll' 'ss' Minimal number of columns to scroll horizontally
|
||||
'sidescrolloff' 'siso' Minimal number of columns to left and right of cursor
|
||||
```
|
||||
|
||||
----------
|
||||
[1] - cursor keys, <End>, <Home>, <PageUp> and <PageDown>
|
||||
## IdeaVim options for IntelliJ-based IDE features
|
||||
|
||||
Some Vim features cannot be implemented by IdeaVim, and must be implemented by the host IDE, such as showing whitespace and line numbers, and enabling soft-wrap.
|
||||
The following options modify equivalent settings and features implemented by IntelliJ-based IDEs.
|
||||
|
||||
There is some mismatch when trying to map Vim options, most of which are local options, to IDE settings, which are mostly global-local.
|
||||
The Vim option will always reflect the effective value of the IDE setting for the current editor, and modifying the Vim option will update the local value of the IDE setting.
|
||||
The default value of the Vim option set during startup is not passed to the IDE setting.
|
||||
|
||||
If the IDE setting has a way to modify the local value, such as entries in the _View | Active Editor_ menu, then changing this will update the current editor and be reflected in the Vim option value.
|
||||
If the IDE setting can only modify its global setting in the main _Settings_ dialog, this change does not always update the current editor (because the local IDE setting has been modified and takes precedence).
|
||||
|
||||
IdeaVim tries to make this work more naturally by updating the editor and local Vim option when a global value changes unless the Vim option has been explicitly set in Command-line mode.
|
||||
|
||||
In other words, if the local Vim value is explicitly set for a window or buffer, interactively, then it should not be reset.
|
||||
If the Vim option was explicitly set in `~/.ideavimrc` however, then the value will be reset, because this can be viewed as a "global" value - set once and applied to subsequently opened windows.
|
||||
(This should not be confused with Vim's concept of global options, which are mainly used to initialise new windows.)
|
||||
|
||||
The local Vim option can always be reset to the global IDE setting value by resetting the Vim option to default with the `:set {option}&` syntax.
|
||||
|
||||
```
|
||||
'bomb' 'bomb' Add or remove a byte order mark (BOM) to the
|
||||
current file. Unlike Vim, the file is modified
|
||||
immediately, and not when saved
|
||||
'breakindent' 'bri' Indent soft wrapped lines to match the first
|
||||
line's indent
|
||||
'colorcolumn' 'cc' Maps to IntelliJ's visual guide columns
|
||||
'cursorline' 'cul' Highlight the line containing the cursor
|
||||
'fileencoding' 'fenc' Change the encoding of the current file. The file
|
||||
is modified and written immediately, rather than
|
||||
waiting to be saved
|
||||
Note that the names of the encoding might not
|
||||
match Vim's known names
|
||||
'fileformat' 'ff' Change the file format - dos, unix or mac
|
||||
The file is modified immediately, rather than
|
||||
when saved
|
||||
'list' 'list' Show whitespace. Maps to the editor's local
|
||||
setting in the View | Active Editor menu
|
||||
'number' 'nu' Show line numbers. Maps to the editor's local
|
||||
setting in the View | Active Editor menu
|
||||
'relativenumber' 'rnu' Show line numbers relative to the current line
|
||||
'textwidth' 'tw' Set the column at which text is automatically
|
||||
wrapped
|
||||
'wrap' 'wrap' Enable soft-wraps. Maps to the editor's local
|
||||
setting in the View | Active Editor menu
|
||||
```
|
||||
|
||||
## IdeaVim only options
|
||||
|
||||
These options are IdeaVim only, and not supported by Vim.
|
||||
They control integration with the host IDE.
|
||||
Unless otherwise stated, these options do not have abbreviations.
|
||||
|
||||
```
|
||||
'ideacopypreprocess' boolean (default off)
|
||||
global or local to buffer
|
||||
When enabled, the IDE will run custom copy pre-processors over text
|
||||
copied to registers. These pre-processors can perform transformations
|
||||
on the text, such as converting escape characters in a string literal
|
||||
into the actual control characters in a Java file.
|
||||
|
||||
This is not usually the expected behaviour, so this option's default
|
||||
value is off. The equivalent processing for paste is controlled by the
|
||||
"ideaput" value to the 'clipboard' option.
|
||||
|
||||
'ideaglobalmode' boolean (default off)
|
||||
global
|
||||
This option will cause IdeaVim to share a single mode across all open
|
||||
windows. In other words, entering Insert mode in one window will
|
||||
enable Insert mode in all windows.
|
||||
|
||||
'ideajoin' boolean (default off)
|
||||
global or local to buffer
|
||||
When enabled, join commands will be handled by the IDE's "smart join"
|
||||
feature. The IDE can change syntax when joining lines, such as merging
|
||||
string literals or if statements. See the wiki for more examples. Not
|
||||
all languages support smart join functionality.
|
||||
|
||||
'ideamarks' boolean (default on)
|
||||
global
|
||||
Maps Vim's global marks to IDE bookmarks.
|
||||
|
||||
'idearefactormode' string (default "select")
|
||||
global or local to buffer
|
||||
Specifies the mode to be used when a refactoring selects text to be
|
||||
edited (e.g. renaming, live template fields, introduce variable, etc):
|
||||
keep Keep the current mode
|
||||
select Switch to Select mode
|
||||
visual Switch to Visual mode
|
||||
|
||||
This option is only used when the refactoring is started in Normal,
|
||||
Insert or Replace mode. Visual or Select modes are not changed.
|
||||
|
||||
'ideastatusicon' string (default "enabled")
|
||||
global
|
||||
This option controls the behaviour and appearance of the IdeaVim icon
|
||||
in the status bar:
|
||||
enabled Show the icon in the status bar
|
||||
gray Show the gray version of the icon
|
||||
disabled Hide the icon
|
||||
|
||||
'ideavimsupport' string (default "dialog")
|
||||
global
|
||||
A comma-separated list of additional buffers or locations where
|
||||
IdeaVim should be enabled:
|
||||
dialog Enable IdeaVim in editors hosted in dialogs
|
||||
singleline Enable IdeaVim in single line editors (not recommended)
|
||||
|
||||
The IDE's editor component can be used in many places, such as VCS
|
||||
commit tool window, or inside dialogs, and even as single line fields.
|
||||
|
||||
'ideawrite' string (default "all")
|
||||
global
|
||||
This option defines the behaviour of the :w command:
|
||||
file Save the current file only
|
||||
all The :w command works like :wa and invokes the Save All
|
||||
IDE action. This allows options such as "Prettier on
|
||||
save" or "ESlint on save" to work with the :w command,
|
||||
but means all files are saved.
|
||||
|
||||
'lookupkeys' string (default "<Tab>,<Down>,<Up>,<Enter>,
|
||||
<Left>,<Right>,<C-Down>,<C-Up>,
|
||||
<PageUp>,<PageDown>, <C-J>,<C-Q>")
|
||||
global
|
||||
Comma-separated list of keys that should be processed by the IDE while
|
||||
a code completion lookup popup is active. For example, <Tab> and
|
||||
<Enter> are used by the IDE to complete the lookup and insert text,
|
||||
but <C-W> should be passed IdeaVim to continue editing the text.
|
||||
|
||||
'trackactionids' boolean (default off)
|
||||
global
|
||||
When on, IdeaVim will try to track the current IDE action and display
|
||||
the action name in a notification. This action ID can then be used in
|
||||
a mapping to the action in the form <Action>(...).
|
||||
|
||||
'visualdelay' number (default 100)
|
||||
global
|
||||
This option specifies the delay, in milliseconds before converting an
|
||||
IDE selection into Visual mode.
|
||||
|
||||
Some IDE features make a selection to help modify text (e.g. backspace
|
||||
in Python or Yaml selects an indent and invokes the "remove selection"
|
||||
action). IdeaVim listens for changes in selection to switch to Visual
|
||||
mode, and will return to Normal mode when the selection is removed,
|
||||
even if originally in Insert mode.
|
||||
|
||||
By waiting before converting to Visual mode, temporary selections can
|
||||
be ignored and the current Vim mode maintained.
|
||||
|
||||
It is not expected that this value will need to be changed.
|
||||
```
|
||||
|
@@ -8,17 +8,30 @@
|
||||
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
ideaVersion=2023.3.3
|
||||
# ideaVersion is the version of the IDE that will be added as a compile-time dependency. The format can be either
|
||||
# product version (e.g. 2024.1, 2024.1.1) or build (e.g. 241.15989.150, 241-EAP-SNAPSHOT). The dependency will be
|
||||
# resolved against the configured repositories, which by default includes Maven releases and snapshots, the CDN used to
|
||||
# download consumer releases, the plugin marketplace and so on.
|
||||
# You can find an example list of all CDN based versions for IDEA Community here:
|
||||
# https://data.services.jetbrains.com/products?code=IC
|
||||
# Maven releases are here: https://www.jetbrains.com/intellij-repository/releases
|
||||
# And snapshots: https://www.jetbrains.com/intellij-repository/snapshots
|
||||
ideaVersion=2024.1.1
|
||||
# Values for type: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
|
||||
ideaType=IC
|
||||
downloadIdeaSources=true
|
||||
instrumentPluginCode=true
|
||||
version=chylex-28
|
||||
version=chylex-36
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.22
|
||||
antlrVersion=4.10.1
|
||||
|
||||
kotlin.incremental.useClasspathSnapshot=false
|
||||
# [VERSION UPDATE] 2024.2 - remove when IdeaVim targets 2024.2
|
||||
# Running IdeaVim in split mode requires 242. Update this version once 242 has been released, and remove it completely
|
||||
# when IdeaVim targets 242, at which point runIdeSplitMode will run correctly with the target version.
|
||||
# See also runIdeSplitMode
|
||||
splitModeVersion=242-EAP-SNAPSHOT
|
||||
|
||||
|
||||
# Please don't forget to update kotlin version in buildscript section
|
||||
# Also update kotlinxSerializationVersion version
|
||||
@@ -28,13 +41,14 @@ publishChannels=eap
|
||||
|
||||
# Kotlinx serialization also uses some version of kotlin stdlib under the hood. However,
|
||||
# we exclude this version from the dependency and use our own version of kotlin that is specified above
|
||||
kotlinxSerializationVersion=1.5.1
|
||||
kotlinxSerializationVersion=1.6.2
|
||||
|
||||
slackUrl=
|
||||
youtrackToken=
|
||||
|
||||
# Gradle settings
|
||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||
org.gradle.caching=true
|
||||
|
||||
# Disable warning from gradle-intellij-plugin. Kotlin stdlib is included as compileOnly, so the warning is unnecessary
|
||||
kotlin.stdlib.default.dependency=false
|
||||
|
99092
qodana.sarif.json
99092
qodana.sarif.json
File diff suppressed because one or more lines are too long
@@ -21,6 +21,9 @@ exclude:
|
||||
- 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
|
||||
- src/main/java/com/maddyhome/idea/vim/group/SearchGroup.java
|
||||
- tests/ui-fixtures
|
||||
dependencyIgnores:
|
||||
- name: "acejump"
|
||||
- name: "icu4j"
|
||||
|
@@ -20,17 +20,17 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.22")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.24")
|
||||
|
||||
implementation("io.ktor:ktor-client-core:2.3.7")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.7")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.7")
|
||||
implementation("io.ktor:ktor-client-core:2.3.12")
|
||||
implementation("io.ktor:ktor-client-cio:2.3.10")
|
||||
implementation("io.ktor:ktor-client-content-negotiation:2.3.10")
|
||||
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.12")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r")
|
||||
|
||||
// This is needed for jgit to connect to ssh
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.8.0.202311291450-r")
|
||||
implementation("org.eclipse.jgit:org.eclipse.jgit.ssh.apache:6.10.0.202406032230-r")
|
||||
implementation("com.vdurmont:semver4j:3.1.0")
|
||||
}
|
||||
|
||||
@@ -58,13 +58,6 @@ tasks.register("checkNewPluginDependencies", JavaExec::class) {
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
}
|
||||
|
||||
tasks.register("updateAffectedRates", JavaExec::class) {
|
||||
group = "verification"
|
||||
description = "This job updates Affected Rate field on YouTrack"
|
||||
mainClass.set("scripts.YouTrackUsersAffectedKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
}
|
||||
|
||||
tasks.register("calculateNewVersion", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.CalculateNewVersionKt")
|
||||
@@ -114,6 +107,13 @@ tasks.register("calculateNewEapVersion", JavaExec::class) {
|
||||
args = listOf("${rootProject.rootDir}")
|
||||
}
|
||||
|
||||
tasks.register("calculateNewEapVersionFromBranch", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.CalculateNewEapVersionFromBranchKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
args = listOf("${rootProject.rootDir}")
|
||||
}
|
||||
|
||||
tasks.register("calculateNewDevVersion", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.CalculateNewDevVersionKt")
|
||||
|
@@ -22,7 +22,7 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||
*/
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val knownPlugins = listOf(
|
||||
val knownPlugins = setOf(
|
||||
"IdeaVimExtension",
|
||||
"github.zgqq.intellij-enhance",
|
||||
"org.jetbrains.IdeaVim-EasyMotion",
|
||||
@@ -31,7 +31,12 @@ val knownPlugins = listOf(
|
||||
"com.github.copilot",
|
||||
"com.github.dankinsoid.multicursor",
|
||||
"com.joshestein.ideavim-quickscope",
|
||||
|
||||
"ca.alexgirard.HarpoonIJ",
|
||||
"me.kyren223.harpoonforjb", // https://plugins.jetbrains.com/plugin/23771-harpoonforjb
|
||||
"com.github.erotourtes.harpoon", // https://plugins.jetbrains.com/plugin/21796-harpooner
|
||||
"me.kyren223.trident", // https://plugins.jetbrains.com/plugin/23818-trident
|
||||
|
||||
"com.protoseo.input-source-auto-converter",
|
||||
|
||||
// "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
|
||||
@@ -42,19 +47,15 @@ suspend fun main() {
|
||||
parameter("dependency", "IdeaVIM")
|
||||
parameter("includeOptional", true)
|
||||
}
|
||||
val output = response.body<List<String>>()
|
||||
val output = response.body<List<String>>().toSet()
|
||||
println(output)
|
||||
if (knownPlugins != output) {
|
||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
val newPlugins = (output - knownPlugins).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
if (newPlugins.isNotEmpty()) {
|
||||
// val removedPlugins = (knownPlugins - output.toSet()).map { it to (getPluginLinkByXmlId(it) ?: "Can't find plugin link") }
|
||||
error(
|
||||
"""
|
||||
|
||||
Unregistered plugins:
|
||||
${if (newPlugins.isNotEmpty()) newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No unregistered plugins"}
|
||||
|
||||
Removed plugins:
|
||||
${if (removedPlugins.isNotEmpty()) removedPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" } else "No removed plugins"}
|
||||
${newPlugins.joinToString(separator = "\n") { it.first + "(" + it.second + ")" }}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
@@ -8,6 +8,12 @@
|
||||
|
||||
package scripts.release
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import org.eclipse.jgit.revwalk.filter.RevFilter
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("HI!")
|
||||
val projectDir = args[0]
|
||||
@@ -19,10 +25,12 @@ fun main(args: Array<String>) {
|
||||
check(branch == "master") {
|
||||
"We should be on master branch"
|
||||
}
|
||||
val mergeBaseCommit = getMergeBaseWithMaster(projectDir, objectId)
|
||||
println("Base commit $mergeBaseCommit")
|
||||
withGit(projectDir) { git ->
|
||||
val log = git.log().setMaxCount(500).call().toList()
|
||||
println("First commit hash in log: " + log.first().name + " log size: ${log.size}")
|
||||
val logDiff = log.takeWhile { it.id.name != objectId.name }
|
||||
val logDiff = log.takeWhile { it.id.name != mergeBaseCommit }
|
||||
val numCommits = logDiff.size
|
||||
println("Log diff size is $numCommits")
|
||||
check(numCommits < 450) {
|
||||
@@ -35,3 +43,18 @@ fun main(args: Array<String>) {
|
||||
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMergeBaseWithMaster(projectDir: String, tag: ObjectId): String {
|
||||
withRepo(projectDir) { repo ->
|
||||
val master = repo.resolve("master")
|
||||
RevWalk(repo).use { walk ->
|
||||
val tagRevCommit = walk.parseCommit(tag)
|
||||
val masterRevCommit = walk.parseCommit(master)
|
||||
walk.setRevFilter(RevFilter.MERGE_BASE)
|
||||
walk.markStart(tagRevCommit)
|
||||
walk.markStart(masterRevCommit)
|
||||
val mergeBase: RevCommit = walk.next()
|
||||
return mergeBase.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package scripts.release
|
||||
|
||||
fun main(args: Array<String> ) {
|
||||
val projectDir = args[0]
|
||||
println("Working directory: $projectDir")
|
||||
val branch = withRepo(projectDir) { it.branch }
|
||||
val (majorBranchVersion, minorBranchVersion) = versions(branch)
|
||||
val versions = getVersionsExistingVersionsFor(majorBranchVersion, minorBranchVersion, projectDir)
|
||||
val maxExistingVersion = versions.keys.maxOrNull()
|
||||
|
||||
val nextVersion = if (maxExistingVersion != null) {
|
||||
if (maxExistingVersion.suffixTokens.isEmpty()) {
|
||||
maxExistingVersion.nextPatch().withSuffix("eap.1").value
|
||||
}
|
||||
else {
|
||||
check(maxExistingVersion.suffixTokens.size == 2) {
|
||||
"We should have exactly two suffix tokens. Current tokens: ${maxExistingVersion.suffixTokens.toList()}"
|
||||
}
|
||||
check(maxExistingVersion.suffixTokens[0] == "eap") {
|
||||
"First suffix token must be eap. Current tokens: ${maxExistingVersion.suffixTokens.toList()}"
|
||||
}
|
||||
|
||||
val newEapNumber = maxExistingVersion.suffixTokens[1].toInt().inc()
|
||||
maxExistingVersion.withSuffix("eap.$newEapNumber").value
|
||||
}
|
||||
} else {
|
||||
"$majorBranchVersion.$minorBranchVersion.0-eap.1"
|
||||
}
|
||||
|
||||
|
||||
println("Next eap version: $nextVersion")
|
||||
println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']")
|
||||
}
|
||||
|
||||
private val regex = "releases/(\\d+)\\.(\\d+)\\.x".toRegex()
|
||||
private fun versions(branchName: String): Pair<Int, Int> {
|
||||
val match = regex.matchEntire(branchName) ?: error("Cannot match branch: $branchName")
|
||||
val major = match.groups[1]
|
||||
val minor = match.groups[2]
|
||||
return major!!.value.toInt() to minor!!.value.toInt()
|
||||
}
|
@@ -64,6 +64,31 @@ enum class ReleaseType {
|
||||
STABLE_NO_PATCH, // Version that ends on 0. Like 2.5.0
|
||||
}
|
||||
|
||||
internal fun getVersionsExistingVersionsFor(
|
||||
majorVersion: Int,
|
||||
minorVersion: Int,
|
||||
projectDir: String,
|
||||
): Map<Semver, ObjectId> {
|
||||
val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build()
|
||||
val git = Git(repository)
|
||||
println(git.log().call().first())
|
||||
println(git.tagList().call().first())
|
||||
|
||||
return git.tagList().call().mapNotNull { ref ->
|
||||
runCatching {
|
||||
// Git has two types of tags: light and annotated. This code detect hash of the commit for both types of tags
|
||||
val revWalk = RevWalk(repository)
|
||||
val tag = revWalk.parseAny(ref.objectId)
|
||||
val commitHash = revWalk.peel(tag).id
|
||||
val semver = Semver(ref.name.removePrefix("refs/tags/"))
|
||||
if (semver.major == majorVersion && semver.minor == minorVersion) {
|
||||
semver to commitHash
|
||||
} else null
|
||||
}.getOrNull()
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
|
||||
internal fun getVersion(projectDir: String, releaseType: ReleaseType): Pair<Semver, ObjectId> {
|
||||
val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build()
|
||||
val git = Git(repository)
|
||||
|
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package scripts
|
||||
|
||||
import io.ktor.client.call.*
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
||||
val areaWeights = setOf(
|
||||
Triple("118-53212", "Plugins", 50),
|
||||
Triple("118-53220", "Vim Script", 30),
|
||||
Triple("118-54084", "Esc", 100),
|
||||
)
|
||||
|
||||
suspend fun updateRates() {
|
||||
println("Updating rates of the issues")
|
||||
areaWeights.forEach { (id, name, weight) ->
|
||||
val unmappedIssues = unmappedIssues(name)
|
||||
println("Got ${unmappedIssues.size} for $name area")
|
||||
|
||||
unmappedIssues.forEach { issueId ->
|
||||
print("Trying to update issue $issueId: ")
|
||||
val response = updateCustomField(issueId) {
|
||||
put("name", "Affected Rate")
|
||||
put("\$type", "SimpleIssueCustomField")
|
||||
put("value", weight)
|
||||
}
|
||||
|
||||
println(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun unmappedIssues(area: String): List<String> {
|
||||
val areaProcessed = if (" " in area) "{$area}" else area
|
||||
val res = issuesQuery(
|
||||
query = "project: VIM Affected Rate: {No affected rate} Area: $areaProcessed #Unresolved",
|
||||
fields = "id,idReadable"
|
||||
)
|
||||
return res.body<JsonArray>().map { it.jsonObject }.map { it["idReadable"]!!.jsonPrimitive.content }
|
||||
}
|
||||
|
||||
suspend fun getAreasWithoutWeight(): Set<Pair<String, String>> {
|
||||
val allAreas = getAreaValues()
|
||||
return allAreas
|
||||
.filterNot { it.key in areaWeights.map { it.first }.toSet() }
|
||||
.entries
|
||||
.map { it.key to it.value }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
suspend fun main() {
|
||||
updateRates()
|
||||
}
|
@@ -1,20 +0,0 @@
|
||||
// Set repository for snapshot versions of gradle plugin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'IdeaVIM'
|
||||
include 'vim-engine'
|
||||
include 'scripts'
|
||||
include 'annotation-processors'
|
||||
include 'tests:java-tests'
|
||||
include 'tests:property-tests'
|
||||
include 'tests:long-running-tests'
|
||||
include 'tests:ui-ij-tests'
|
||||
include 'tests:ui-py-tests'
|
||||
include 'tests:ui-fixtures'
|
21
settings.gradle.kts
Normal file
21
settings.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Set repository for snapshot versions of gradle plugin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "IdeaVIM"
|
||||
|
||||
include("vim-engine")
|
||||
include("scripts")
|
||||
include("annotation-processors")
|
||||
include("tests:java-tests")
|
||||
include("tests:property-tests")
|
||||
include("tests:long-running-tests")
|
||||
include("tests:ui-ij-tests")
|
||||
include("tests:ui-py-tests")
|
||||
include("tests:ui-fixtures")
|
@@ -11,7 +11,6 @@ package com.maddyhome.idea.vim;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.ShortcutSet;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction;
|
||||
@@ -80,14 +79,6 @@ public class EventFacade {
|
||||
action.unregisterCustomShortcutSet(component);
|
||||
}
|
||||
|
||||
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||
document.addDocumentListener(listener);
|
||||
}
|
||||
|
||||
public void removeDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||
document.removeDocumentListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable) {
|
||||
EditorFactory.getInstance().addEditorFactoryListener(listener, parentDisposable);
|
||||
}
|
||||
@@ -98,20 +89,12 @@ public class EventFacade {
|
||||
editor.getCaretModel().addCaretListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeCaretListener(@NotNull Editor editor, @NotNull CaretListener listener) {
|
||||
editor.getCaretModel().removeCaretListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorMouseListener(@NotNull Editor editor,
|
||||
@NotNull EditorMouseListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorMouseListener(@NotNull Editor editor, @NotNull EditorMouseListener listener) {
|
||||
editor.removeEditorMouseListener(listener);
|
||||
}
|
||||
|
||||
public void addComponentMouseListener(@NotNull Component component,
|
||||
@NotNull MouseListener mouseListener,
|
||||
@NotNull Disposable disposable) {
|
||||
@@ -119,30 +102,18 @@ public class EventFacade {
|
||||
Disposer.register(disposable, () -> component.removeMouseListener(mouseListener));
|
||||
}
|
||||
|
||||
public void removeComponentMouseListener(@NotNull Component component, @NotNull MouseListener mouseListener) {
|
||||
component.removeMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
public void addEditorMouseMotionListener(@NotNull Editor editor,
|
||||
@NotNull EditorMouseMotionListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.addEditorMouseMotionListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorMouseMotionListener(@NotNull Editor editor, @NotNull EditorMouseMotionListener listener) {
|
||||
editor.removeEditorMouseMotionListener(listener);
|
||||
}
|
||||
|
||||
public void addEditorSelectionListener(@NotNull Editor editor,
|
||||
@NotNull SelectionListener listener,
|
||||
@NotNull Disposable disposable) {
|
||||
editor.getSelectionModel().addSelectionListener(listener, disposable);
|
||||
}
|
||||
|
||||
public void removeEditorSelectionListener(@NotNull Editor editor, @NotNull SelectionListener listener) {
|
||||
editor.getSelectionModel().removeSelectionListener(listener);
|
||||
}
|
||||
|
||||
private @NotNull TypedAction getTypedAction() {
|
||||
return TypedAction.getInstance();
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ import com.intellij.openapi.project.ProjectManagerListener
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
|
||||
/**
|
||||
@@ -36,8 +36,10 @@ internal class PluginStartup : ProjectActivity/*, LightEditCompatible*/ {
|
||||
// This is a temporal workaround for VIM-2487
|
||||
internal class PyNotebooksCloseWorkaround : ProjectManagerListener {
|
||||
override fun projectClosingBeforeSave(project: Project) {
|
||||
// TODO: Confirm context in CWM scenario
|
||||
if (injector.globalIjOptions().closenotebooks) {
|
||||
localEditors().forEach { editor ->
|
||||
injector.editorGroup.getEditors().forEach { vimEditor ->
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val virtualFile = EditorHelper.getVirtualFile(editor)
|
||||
if (virtualFile?.extension == "ipynb") {
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
|
@@ -14,44 +14,43 @@ import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
public object RegisterActions {
|
||||
object RegisterActions {
|
||||
/**
|
||||
* Register all the key/action mappings for the plugin.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun registerActions() {
|
||||
fun registerActions() {
|
||||
registerVimCommandActions()
|
||||
registerEmptyShortcuts() // todo most likely it is not needed
|
||||
registerShortcutsWithoutActions()
|
||||
}
|
||||
|
||||
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
fun findAction(id: String): EditorActionHandlerBase? {
|
||||
val commandBean = IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: EngineCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
return commandBean.instance
|
||||
}
|
||||
|
||||
public fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||
fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||
return findAction(id) ?: throw RuntimeException("Action $id is not registered")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun unregisterActions() {
|
||||
fun unregisterActions() {
|
||||
val keyGroup = VimPlugin.getKeyIfCreated()
|
||||
keyGroup?.unregisterCommandActions()
|
||||
}
|
||||
|
||||
private fun registerVimCommandActions() {
|
||||
val parser = VimPlugin.getKey()
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
}
|
||||
|
||||
private fun registerEmptyShortcuts() {
|
||||
private fun registerShortcutsWithoutActions() {
|
||||
val parser = VimPlugin.getKey()
|
||||
|
||||
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
|
||||
// still need to register the shortcut, to make sure the editor doesn't swallow it.
|
||||
parser
|
||||
.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
|
||||
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.System)
|
||||
}
|
||||
}
|
||||
|
@@ -37,8 +37,8 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
|
||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimSearchGroup;
|
||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
||||
import com.maddyhome.idea.vim.yank.YankGroupBase;
|
||||
import org.jdom.Element;
|
||||
@@ -46,6 +46,7 @@ import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||
import static com.maddyhome.idea.vim.vimscript.services.VimRcService.executeIdeaVimRc;
|
||||
@@ -123,12 +124,12 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return (FileGroup)VimInjectorKt.getInjector().getFile();
|
||||
}
|
||||
|
||||
public static @NotNull SearchGroup getSearch() {
|
||||
return ApplicationManager.getApplication().getService(SearchGroup.class);
|
||||
public static @NotNull IjVimSearchGroup getSearch() {
|
||||
return ApplicationManager.getApplication().getService(IjVimSearchGroup.class);
|
||||
}
|
||||
|
||||
public static @Nullable SearchGroup getSearchIfCreated() {
|
||||
return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
|
||||
public static @Nullable IjVimSearchGroup getSearchIfCreated() {
|
||||
return ApplicationManager.getApplication().getServiceIfCreated(IjVimSearchGroup.class);
|
||||
}
|
||||
|
||||
public static @NotNull ProcessGroup getProcess() {
|
||||
@@ -211,22 +212,22 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
public static void setEnabled(final boolean enabled) {
|
||||
if (isEnabled() == enabled) return;
|
||||
|
||||
if (!enabled) {
|
||||
getInstance().turnOffPlugin(true);
|
||||
}
|
||||
|
||||
getInstance().enabled = enabled;
|
||||
|
||||
if (enabled) {
|
||||
getInstance().turnOnPlugin();
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOn();
|
||||
} else {
|
||||
VimInjectorKt.getInjector().getListenersNotifier().notifyPluginTurnedOff();
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
getInstance().turnOffPlugin(true);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
getInstance().turnOnPlugin();
|
||||
}
|
||||
|
||||
StatusBarIconFactory.Util.INSTANCE.updateIcon();
|
||||
}
|
||||
|
||||
@@ -282,7 +283,13 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
ideavimrcRegistered = true;
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
executeIdeaVimRc(editor);
|
||||
try {
|
||||
injector.getOptionGroup().startInitVimRc();
|
||||
executeIdeaVimRc(editor);
|
||||
}
|
||||
finally {
|
||||
injector.getOptionGroup().endInitVimRc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,20 +346,21 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
}
|
||||
|
||||
private void turnOffPlugin(boolean unsubscribe) {
|
||||
SearchGroup searchGroup = getSearchIfCreated();
|
||||
IjVimSearchGroup searchGroup = getSearchIfCreated();
|
||||
if (searchGroup != null) {
|
||||
searchGroup.turnOff();
|
||||
}
|
||||
if (unsubscribe) {
|
||||
VimListenerManager.INSTANCE.turnOff();
|
||||
}
|
||||
ExEntryPanel.fullReset();
|
||||
injector.getCommandLine().fullReset();
|
||||
|
||||
// Unregister vim actions in command mode
|
||||
RegisterActions.unregisterActions();
|
||||
|
||||
if (onOffDisposable != null) {
|
||||
Disposer.dispose(onOffDisposable);
|
||||
onOffDisposable = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,13 +12,13 @@ import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
|
||||
@Service
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class VimProjectService(val project: Project) : Disposable {
|
||||
override fun dispose() {
|
||||
// Not sure if this is a best solution
|
||||
EditorHolderService.getInstance().editor = null
|
||||
ExEntryPanel.getInstance().editor = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@@ -32,7 +32,7 @@ import javax.swing.KeyStroke
|
||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||
* way to get ideavim keys for this plugin. See VIM-3085
|
||||
*/
|
||||
public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
||||
class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandlerEx {
|
||||
private val handler = KeyHandler.getInstance()
|
||||
private val traceTime = injector.globalOptions().ideatracetime
|
||||
|
||||
@@ -77,7 +77,7 @@ public class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActio
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
val startTime = if (traceTime) System.currentTimeMillis() else null
|
||||
handler.handleKey(editor.vim, keyStroke, injector.executionContextManager.onEditor(editor.vim, context.vim))
|
||||
handler.handleKey(editor.vim, keyStroke, context.vim, handler.keyHandlerState)
|
||||
if (startTime != null) {
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
||||
|
@@ -8,6 +8,6 @@
|
||||
|
||||
package com.maddyhome.idea.vim.action
|
||||
|
||||
public object IntellijCommandProvider : CommandProvider {
|
||||
object IntellijCommandProvider : CommandProvider {
|
||||
override val commandListFileName: String = "intellij_commands.json"
|
||||
}
|
39
src/main/java/com/maddyhome/idea/vim/action/RedrawAction.kt
Normal file
39
src/main/java/com/maddyhome/idea/vim/action/RedrawAction.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.action
|
||||
|
||||
import com.intellij.vim.annotations.CommandOrMotion
|
||||
import com.intellij.vim.annotations.Mode
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
|
||||
/**
|
||||
* Clear and redraw the screen
|
||||
*
|
||||
* Typically, an IDE does not need to redraw the screen - it's editor handles scrolling, etc. However, it can be
|
||||
* necessary to clear the status line.
|
||||
*/
|
||||
@CommandOrMotion(keys = ["<C-L>"], modes = [Mode.NORMAL])
|
||||
internal class RedrawAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments,
|
||||
): Boolean {
|
||||
injector.redrawService.redraw()
|
||||
return true
|
||||
}
|
||||
}
|
@@ -44,7 +44,8 @@ import com.maddyhome.idea.vim.listener.AceJumpService
|
||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates.appCodeTemplateCaptured
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.ui.ex.ExTextField
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.KeyEvent
|
||||
@@ -59,7 +60,7 @@ import javax.swing.KeyStroke
|
||||
* This class is used in Which-Key plugin, so don't make it internal. Generally, we should provide a proper
|
||||
* way to get ideavim keys for this plugin. See VIM-3085
|
||||
*/
|
||||
public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||
class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||
private val traceTime: Boolean
|
||||
get() {
|
||||
// Make sure the injector is initialized
|
||||
@@ -79,11 +80,8 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
||||
// Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
|
||||
try {
|
||||
val start = if (traceTime) System.currentTimeMillis() else null
|
||||
KeyHandler.getInstance().handleKey(
|
||||
editor.vim,
|
||||
keyStroke,
|
||||
injector.executionContextManager.onEditor(editor.vim, e.dataContext.vim),
|
||||
)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keyHandler.handleKey(editor.vim, keyStroke, e.dataContext.vim, keyHandler.keyHandlerState)
|
||||
if (start != null) {
|
||||
val duration = System.currentTimeMillis() - start
|
||||
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
||||
@@ -256,7 +254,14 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getEditor(e: AnActionEvent): Editor? = e.getData(PlatformDataKeys.EDITOR)
|
||||
private fun getEditor(e: AnActionEvent): Editor? {
|
||||
return e.getData(PlatformDataKeys.EDITOR)
|
||||
?: if (e.getData(PlatformDataKeys.CONTEXT_COMPONENT) is ExTextField) {
|
||||
ExEntryPanel.getInstance().editor
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time the key pressed with an active lookup, there is a decision:
|
||||
@@ -380,6 +385,10 @@ private class ActionEnableStatus(
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ActionEnableStatus(isEnabled=$isEnabled, message='$message', logLevel=$logLevel)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<ActionEnableStatus>()
|
||||
|
||||
|
@@ -14,6 +14,7 @@ import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
@@ -21,6 +22,7 @@ import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.argumentCaptured
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
@@ -29,21 +31,67 @@ import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimFuncref
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.FunctionCallExpression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.SimpleExpression
|
||||
|
||||
// todo make it multicaret
|
||||
private fun doOperatorAction(editor: VimEditor, context: ExecutionContext, textRange: TextRange, selectionType: SelectionType): Boolean {
|
||||
val operatorFunction = injector.keyGroup.operatorFunction
|
||||
if (operatorFunction == null) {
|
||||
val func = injector.globalOptions().operatorfunc
|
||||
if (func.isEmpty()) {
|
||||
VimPlugin.showMessage(MessageHelper.message("E774"))
|
||||
return false
|
||||
}
|
||||
|
||||
val scriptContext = CommandLineVimLContext
|
||||
|
||||
// The option value is either a function name, which should have a handler, or it might be a lambda expression, or a
|
||||
// `function` or `funcref` call expression, all of which will return a funcref (with a handler)
|
||||
var handler = injector.functionService.getFunctionHandlerOrNull(null, func, scriptContext)
|
||||
if (handler == null) {
|
||||
val expression = injector.vimscriptParser.parseExpression(func)
|
||||
if (expression != null) {
|
||||
try {
|
||||
val value = expression.evaluate(editor, context, scriptContext)
|
||||
if (value is VimFuncref) {
|
||||
handler = value.handler
|
||||
}
|
||||
} catch (ex: ExException) {
|
||||
// Get the argument for function('...') or funcref('...') for the error message
|
||||
val functionName = if (expression is FunctionCallExpression && expression.arguments.size > 0) {
|
||||
expression.arguments[0].evaluate(editor, context, scriptContext).toString()
|
||||
}
|
||||
else {
|
||||
func
|
||||
}
|
||||
|
||||
VimPlugin.showMessage("E117: Unknown function: $functionName")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handler == null) {
|
||||
VimPlugin.showMessage("E117: Unknown function: $func")
|
||||
return false
|
||||
}
|
||||
|
||||
val arg = when (selectionType) {
|
||||
SelectionType.LINE_WISE -> "line"
|
||||
SelectionType.CHARACTER_WISE -> "char"
|
||||
SelectionType.BLOCK_WISE -> "block"
|
||||
}
|
||||
|
||||
val saveRepeatHandler = VimRepeater.repeatHandler
|
||||
injector.markService.setChangeMarks(editor.primaryCaret(), textRange)
|
||||
KeyHandler.getInstance().reset(editor)
|
||||
val result = operatorFunction.apply(editor, context, selectionType)
|
||||
|
||||
val arguments = listOf(SimpleExpression(arg))
|
||||
handler.executeFunction(arguments, editor, context, scriptContext)
|
||||
|
||||
VimRepeater.repeatHandler = saveRepeatHandler
|
||||
return result
|
||||
return true
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["g@"], modes = [Mode.NORMAL])
|
||||
|
@@ -7,9 +7,9 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.action.change
|
||||
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.vim.annotations.CommandOrMotion
|
||||
import com.intellij.vim.annotations.Mode
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
@@ -58,11 +58,11 @@ internal class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
||||
)
|
||||
} else if (!repeatHandler && lastCommand != null) {
|
||||
if (cmd.rawCount > 0) {
|
||||
lastCommand.count = cmd.count
|
||||
lastCommand.rawCount = cmd.count
|
||||
val arg = lastCommand.argument
|
||||
if (arg != null) {
|
||||
val mot = arg.motion
|
||||
mot.count = 0
|
||||
mot.rawCount = 0
|
||||
}
|
||||
}
|
||||
state.executingCommand = lastCommand
|
||||
|
@@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
|
||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.NORMAL])
|
||||
public class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
||||
class DeleteJoinLinesAction : ChangeEditorActionHandler.ConditionalSingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
override fun runAsMulticaret(
|
||||
editor: VimEditor,
|
||||
|
@@ -19,7 +19,7 @@ import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||
import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
|
||||
@CommandOrMotion(keys = ["J"], modes = [Mode.NORMAL])
|
||||
public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun execute(
|
||||
@@ -34,7 +34,7 @@ public class DeleteJoinLinesSpacesAction : ChangeEditorActionHandler.SingleExecu
|
||||
}
|
||||
injector.editorGroup.notifyIdeaJoin(editor)
|
||||
var res = true
|
||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
if (!injector.changeGroup.deleteJoinLines(editor, caret, operatorArguments.count1, true, operatorArguments)) {
|
||||
res = false
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
* @author vlan
|
||||
*/
|
||||
@CommandOrMotion(keys = ["gJ"], modes = [Mode.VISUAL])
|
||||
public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun executeForAllCarets(
|
||||
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesAction : VisualOperatorActionHandler.SingleExe
|
||||
return true
|
||||
}
|
||||
var res = true
|
||||
editor.nativeCarets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
editor.nativeCarets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
if (!caret.isValid) return@forEach
|
||||
val range = caretsAndSelections[caret] ?: return@forEach
|
||||
if (!injector.changeGroup.deleteJoinRange(
|
||||
|
@@ -23,7 +23,7 @@ import com.maddyhome.idea.vim.newapi.ijOptions
|
||||
* @author vlan
|
||||
*/
|
||||
@CommandOrMotion(keys = ["J"], modes = [Mode.VISUAL])
|
||||
public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
|
||||
override fun executeForAllCarets(
|
||||
@@ -39,7 +39,7 @@ public class DeleteJoinVisualLinesSpacesAction : VisualOperatorActionHandler.Sin
|
||||
return true
|
||||
}
|
||||
var res = true
|
||||
editor.carets().sortedByDescending { it.offset.point }.forEach { caret ->
|
||||
editor.carets().sortedByDescending { it.offset }.forEach { caret ->
|
||||
if (!caret.isValid) return@forEach
|
||||
val range = caretsAndSelections[caret] ?: return@forEach
|
||||
if (!injector.changeGroup.deleteJoinRange(
|
||||
|
@@ -36,6 +36,18 @@ internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELET
|
||||
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN) {
|
||||
override val type: Command.Type = Command.Type.MOTION
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments
|
||||
): Boolean {
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||
return super.execute(editor, context, cmd, operatorArguments)
|
||||
}
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
|
||||
@@ -48,6 +60,18 @@ internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
|
||||
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP) {
|
||||
override val type: Command.Type = Command.Type.MOTION
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_CLEAR_STROKES)
|
||||
|
||||
override fun execute(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
cmd: Command,
|
||||
operatorArguments: OperatorArguments
|
||||
): Boolean {
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
editor.forEachCaret { undo.endInsertSequence(it, it.offset, nanoTime) }
|
||||
return super.execute(editor, context, cmd, operatorArguments)
|
||||
}
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["K"], modes = [Mode.NORMAL])
|
||||
|
@@ -1,35 +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.action.ex
|
||||
|
||||
import com.intellij.vim.annotations.CommandOrMotion
|
||||
import com.intellij.vim.annotations.Mode
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Called by KeyHandler to process the contents of the ex entry panel
|
||||
*
|
||||
* The mapping for this action means that the ex command is executed as a write action
|
||||
*/
|
||||
@CommandOrMotion(keys = ["<CR>", "<C-M>", "<C-J>"], modes = [Mode.CMD_LINE])
|
||||
public class ProcessExEntryAction : VimActionHandler.SingleExecution() {
|
||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||
|
||||
override val flags: EnumSet<CommandFlags> = EnumSet.of(CommandFlags.FLAG_COMPLETE_EX)
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, cmd: Command, operatorArguments: OperatorArguments): Boolean {
|
||||
return VimPlugin.getProcess().processExEntry(editor, context)
|
||||
}
|
||||
}
|
@@ -1,72 +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.command
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Additional class
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
public class CommandState(private val machine: VimStateMachine) {
|
||||
|
||||
public val isOperatorPending: Boolean
|
||||
get() = machine.isOperatorPending
|
||||
|
||||
public val mode: CommandState.Mode
|
||||
get() {
|
||||
val myMode = machine.mode
|
||||
return when (myMode) {
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> CommandState.Mode.CMD_LINE
|
||||
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> CommandState.Mode.INSERT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> CommandState.Mode.COMMAND
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> CommandState.Mode.OP_PENDING
|
||||
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> CommandState.Mode.REPLACE
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> CommandState.Mode.SELECT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> CommandState.Mode.VISUAL
|
||||
}
|
||||
}
|
||||
|
||||
public val commandBuilder: CommandBuilder
|
||||
get() = machine.commandBuilder
|
||||
|
||||
public val mappingState: MappingState
|
||||
get() = machine.mappingState
|
||||
|
||||
public enum class Mode {
|
||||
// Basic modes
|
||||
COMMAND, VISUAL, SELECT, INSERT, CMD_LINE, /*EX*/
|
||||
|
||||
// Additional modes
|
||||
OP_PENDING, REPLACE /*, VISUAL_REPLACE*/, INSERT_NORMAL, INSERT_VISUAL, INSERT_SELECT
|
||||
}
|
||||
|
||||
public enum class SubMode {
|
||||
NONE, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||
}
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
public fun getInstance(editor: Editor): CommandState {
|
||||
return CommandState(editor.vim.vimStateMachine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val CommandState.SubMode.engine: SelectionType
|
||||
get() = when (this) {
|
||||
CommandState.SubMode.NONE -> error("Unexpected value")
|
||||
CommandState.SubMode.VISUAL_CHARACTER -> SelectionType.CHARACTER_WISE
|
||||
CommandState.SubMode.VISUAL_LINE -> SelectionType.LINE_WISE
|
||||
CommandState.SubMode.VISUAL_BLOCK -> SelectionType.BLOCK_WISE
|
||||
}
|
@@ -12,18 +12,18 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
|
||||
public class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
||||
public fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
||||
class CharacterPosition(line: Int, col: Int) : LogicalPosition(line, col) {
|
||||
fun toOffset(editor: Editor): Int = editor.vim.getLineStartOffset(line) + column
|
||||
|
||||
public companion object {
|
||||
public fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
||||
companion object {
|
||||
fun fromOffset(editor: Editor, offset: Int): CharacterPosition {
|
||||
// logical position "expands" tabs
|
||||
val logicalPosition = editor.offsetToLogicalPosition(offset)
|
||||
val lineStartOffset = editor.vim.getLineStartOffset(logicalPosition.line)
|
||||
return CharacterPosition(logicalPosition.line, offset - lineStartOffset)
|
||||
}
|
||||
|
||||
public fun atCaret(editor: Editor): CharacterPosition {
|
||||
fun atCaret(editor: Editor): CharacterPosition {
|
||||
return fromOffset(editor, editor.caretModel.offset)
|
||||
}
|
||||
}
|
||||
|
@@ -9,8 +9,6 @@
|
||||
package com.maddyhome.idea.vim.common
|
||||
|
||||
import com.intellij.application.options.CodeStyle
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptions
|
||||
@@ -39,13 +37,12 @@ internal class IndentConfig private constructor(indentOptions: IndentOptions) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(editor: Editor, context: DataContext): IndentConfig {
|
||||
return create(editor, PlatformDataKeys.PROJECT.getData(context))
|
||||
fun create(editor: Editor): IndentConfig {
|
||||
return create(editor, editor.project)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun create(editor: Editor, project: Project? = editor.project): IndentConfig {
|
||||
fun create(editor: Editor, project: Project?): IndentConfig {
|
||||
val indentOptions = if (project != null) {
|
||||
CodeStyle.getIndentOptions(project, editor.document)
|
||||
} else {
|
||||
|
@@ -13,30 +13,56 @@ import com.maddyhome.idea.vim.api.VimExOutputPanel
|
||||
import com.maddyhome.idea.vim.helper.vimExOutput
|
||||
import com.maddyhome.idea.vim.ui.ExOutputPanel
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
|
||||
// TODO: We need a nicer way to handle output, especially wrt testing, appending + clearing
|
||||
class ExOutputModel private constructor(private val myEditor: Editor) : VimExOutputPanel {
|
||||
private var isActiveInTestMode = false
|
||||
|
||||
override val isActive: Boolean
|
||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.isPanelActive(myEditor)
|
||||
} else {
|
||||
isActiveInTestMode
|
||||
}
|
||||
|
||||
override var text: String? = null
|
||||
private set
|
||||
get() = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).text
|
||||
} else {
|
||||
// ExOutputPanel always returns a non-null string
|
||||
field ?: ""
|
||||
}
|
||||
set(value) {
|
||||
// ExOutputPanel will strip a trailing newline. We'll do it now so that tests have the same behaviour. We also
|
||||
// never pass null to ExOutputPanel, but we do store it for tests, so we know if we're active or not
|
||||
val newValue = value?.removeSuffix("\n")
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).setText(newValue ?: "")
|
||||
} else {
|
||||
field = newValue
|
||||
isActiveInTestMode = !newValue.isNullOrEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun output(text: String) {
|
||||
this.text = text
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).setText(text)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
text = null
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
ExOutputPanel.getInstance(myEditor).deactivate(false)
|
||||
ExOutputPanel.getInstance(myEditor).close()
|
||||
}
|
||||
else {
|
||||
isActiveInTestMode = false
|
||||
}
|
||||
}
|
||||
|
||||
public companion object {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
public fun getInstance(editor: Editor): ExOutputModel {
|
||||
fun getInstance(editor: Editor): ExOutputModel {
|
||||
var model = editor.vimExOutput
|
||||
if (model == null) {
|
||||
model = ExOutputModel(editor)
|
||||
@@ -44,5 +70,8 @@ public class ExOutputModel private constructor(private val myEditor: Editor) : V
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun tryGetInstance(editor: Editor) = editor.vimExOutput
|
||||
}
|
||||
}
|
||||
|
@@ -7,28 +7,40 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.extension
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.action.change.Extension
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.common.CommandAlias
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.helper.CommandLineHelper
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||
import com.maddyhome.idea.vim.helper.noneOfEnum
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||
import com.maddyhome.idea.vim.vimscript.model.Executable
|
||||
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
|
||||
import com.maddyhome.idea.vim.vimscript.model.VimLContext
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Expression
|
||||
import com.maddyhome.idea.vim.vimscript.model.expressions.Scope
|
||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionDeclaration
|
||||
import com.maddyhome.idea.vim.vimscript.model.statements.FunctionFlag
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
@@ -38,13 +50,13 @@ import javax.swing.KeyStroke
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public object VimExtensionFacade {
|
||||
object VimExtensionFacade {
|
||||
|
||||
private val LOG = logger<VimExtensionFacade>()
|
||||
|
||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||
@JvmStatic
|
||||
public fun putExtensionHandlerMapping(
|
||||
fun putExtensionHandlerMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -55,13 +67,15 @@ public object VimExtensionFacade {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Additional method
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||
@JvmStatic
|
||||
public fun putExtensionHandlerMapping(
|
||||
@Deprecated(
|
||||
"Use VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||
ReplaceWith(
|
||||
"VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)",
|
||||
"com.maddyhome.idea.vim.VimPlugin"
|
||||
)
|
||||
)
|
||||
fun putExtensionHandlerMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -73,7 +87,7 @@ public object VimExtensionFacade {
|
||||
|
||||
/** The 'map' command for mapping keys to other keys. */
|
||||
@JvmStatic
|
||||
public fun putKeyMapping(
|
||||
fun putKeyMapping(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -85,7 +99,7 @@ public object VimExtensionFacade {
|
||||
|
||||
/** The 'map' command for mapping keys to other keys if there is no other mapping to these keys */
|
||||
@JvmStatic
|
||||
public fun putKeyMappingIfMissing(
|
||||
fun putKeyMappingIfMissing(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
@@ -99,7 +113,7 @@ public object VimExtensionFacade {
|
||||
/**
|
||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||
*/
|
||||
public fun addCommand(
|
||||
fun addCommand(
|
||||
name: String,
|
||||
handler: CommandAliasHandler,
|
||||
) {
|
||||
@@ -110,7 +124,7 @@ public object VimExtensionFacade {
|
||||
* Equivalent to calling 'command' to set up a user-defined command or alias
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun addCommand(
|
||||
fun addCommand(
|
||||
name: String,
|
||||
minimumNumberOfArguments: Int,
|
||||
maximumNumberOfArguments: Int,
|
||||
@@ -120,12 +134,6 @@ public object VimExtensionFacade {
|
||||
.setAlias(name, CommandAlias.Call(minimumNumberOfArguments, maximumNumberOfArguments, name, handler))
|
||||
}
|
||||
|
||||
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
|
||||
@JvmStatic
|
||||
public fun setOperatorFunction(function: OperatorFunction) {
|
||||
VimPlugin.getKey().operatorFunction = function
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs normal mode commands similar to ':normal! {commands}'.
|
||||
* Mappings doesn't work with this function
|
||||
@@ -134,14 +142,15 @@ public object VimExtensionFacade {
|
||||
* leaves the editor in the insert mode if it's been activated.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
val context = injector.executionContextManager.onEditor(editor.vim)
|
||||
keys.forEach { KeyHandler.getInstance().handleKey(editor.vim, it, context, false, false) }
|
||||
fun executeNormalWithoutMapping(keys: List<KeyStroke>, editor: Editor) {
|
||||
val context = injector.executionContextManager.getEditorExecutionContext(editor.vim)
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
keys.forEach { keyHandler.handleKey(editor.vim, it, context, false, false, keyHandler.keyHandlerState) }
|
||||
}
|
||||
|
||||
/** Returns a single key stroke from the user input similar to 'getchar()'. */
|
||||
@JvmStatic
|
||||
public fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||
fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||
if (editor.vim.vimStateMachine.isDotRepeatInProgress) {
|
||||
val input = Extension.consumeKeystroke()
|
||||
LOG.trace("inputKeyStroke: dot repeat in progress. Input: $input")
|
||||
@@ -152,7 +161,7 @@ public object VimExtensionFacade {
|
||||
LOG.trace("Unit test mode is active")
|
||||
val mappingStack = KeyHandler.getInstance().keyStack
|
||||
mappingStack.feedSomeStroke() ?: TestInputModel.getInstance(editor).nextKeyStroke()?.also {
|
||||
if (editor.vim.vimStateMachine.isRecording) {
|
||||
if (injector.registerGroup.isRecording) {
|
||||
KeyHandler.getInstance().modalEntryKeys += it
|
||||
}
|
||||
}
|
||||
@@ -173,38 +182,99 @@ public object VimExtensionFacade {
|
||||
|
||||
/** Returns a string typed in the input box similar to 'input()'. */
|
||||
@JvmStatic
|
||||
public fun inputString(editor: Editor, prompt: String, finishOn: Char?): String {
|
||||
return service<CommandLineHelper>().inputString(editor.vim, prompt, finishOn) ?: ""
|
||||
fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||
return injector.commandLine.inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||
}
|
||||
|
||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||
@JvmStatic
|
||||
public fun getRegister(register: Char): List<KeyStroke>? {
|
||||
fun getRegister(register: Char): List<KeyStroke>? {
|
||||
val reg = VimPlugin.getRegister().getRegister(register) ?: return null
|
||||
return reg.keys
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||
val reg = caret.registerStorage.getRegister(register) ?: return null
|
||||
fun getRegisterForCaret(register: Char, caret: VimCaret): List<KeyStroke>? {
|
||||
val reg = injector.registerGroup.getRegister(register) ?: return null
|
||||
return reg.keys
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||
caret.registerStorage.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
fun setRegisterForCaret(register: Char, caret: ImmutableVimCaret, keys: List<KeyStroke?>?) {
|
||||
injector.registerGroup.setKeys(register, keys?.filterNotNull() ?: emptyList())
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun exportScriptFunction(
|
||||
scope: Scope?,
|
||||
name: String,
|
||||
args: List<String>,
|
||||
defaultArgs: List<Pair<String, Expression>>,
|
||||
hasOptionalArguments: Boolean,
|
||||
flags: EnumSet<FunctionFlag>,
|
||||
function: ScriptFunction
|
||||
) {
|
||||
var functionDeclaration: FunctionDeclaration? = null
|
||||
val body = listOf(object : Executable {
|
||||
// This context is set to the function declaration during initialisation and then set to the function execution
|
||||
// context during execution
|
||||
override lateinit var vimContext: VimLContext
|
||||
override var rangeInScript: TextRange = TextRange(0, 0)
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext): ExecutionResult {
|
||||
return function.execute(editor, context, functionDeclaration!!.functionVariables)
|
||||
}
|
||||
})
|
||||
functionDeclaration = FunctionDeclaration(
|
||||
scope,
|
||||
name,
|
||||
args,
|
||||
defaultArgs,
|
||||
body,
|
||||
replaceExisting = true,
|
||||
flags,
|
||||
hasOptionalArguments
|
||||
)
|
||||
functionDeclaration.rangeInScript = TextRange(0, 0)
|
||||
body.forEach { it.vimContext = functionDeclaration }
|
||||
injector.functionService.storeFunction(functionDeclaration)
|
||||
}
|
||||
}
|
||||
|
||||
fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFunction) {
|
||||
exportScriptFunction(null, name, listOf("type"), emptyList(), false, noneOfEnum()) {
|
||||
editor, context, args ->
|
||||
|
||||
val type = args["type"]?.asString()
|
||||
val selectionType = when (type) {
|
||||
"line" -> SelectionType.LINE_WISE
|
||||
"block" -> SelectionType.BLOCK_WISE
|
||||
"char" -> SelectionType.CHARACTER_WISE
|
||||
else -> return@exportScriptFunction ExecutionResult.Error
|
||||
}
|
||||
|
||||
if (function.apply(editor, context, selectionType)) {
|
||||
ExecutionResult.Success
|
||||
}
|
||||
else {
|
||||
ExecutionResult.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface ScriptFunction {
|
||||
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||
}
|
||||
|
@@ -19,12 +19,12 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
* COMPATIBILITY-LAYER: Created a class, renamed original class
|
||||
* Please see: https://jb.gg/zo8n0r
|
||||
*/
|
||||
public interface VimExtensionHandler : ExtensionHandler {
|
||||
interface VimExtensionHandler : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
execute(editor.ij, context.ij)
|
||||
}
|
||||
|
||||
public fun execute(editor: Editor, context: DataContext)
|
||||
fun execute(editor: Editor, context: DataContext)
|
||||
|
||||
public abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
||||
abstract class WithCallback : ExtensionHandler.WithCallback(), VimExtensionHandler
|
||||
}
|
||||
|
@@ -67,7 +67,7 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(option) {
|
||||
if (injector.optionGroup.getOptionValue(option, OptionAccessScope.GLOBAL(null)).asBoolean()) {
|
||||
initExtension(extensionBean, name)
|
||||
PluginState.enabledExtensions.add(name)
|
||||
PluginState.Util.enabledExtensions.add(name)
|
||||
} else {
|
||||
extensionBean.instance.dispose()
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.argtextobj;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.command.*;
|
||||
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -244,19 +245,18 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
|
||||
IjVimEditor vimEditor = (IjVimEditor) editor;
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(((IjVimCaret)caret).getCaret(), range.getStartOffset());
|
||||
@@ -265,7 +265,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
}
|
||||
|
@@ -18,30 +18,32 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.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.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.command.TextObjectVisualType
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||
import com.maddyhome.idea.vim.ex.ranges.Range
|
||||
import com.maddyhome.idea.vim.ex.ranges.toTextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.addCommand
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
@@ -49,17 +51,19 @@ import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import java.util.*
|
||||
|
||||
internal class CommentaryExtension : VimExtension {
|
||||
|
||||
companion object {
|
||||
object Util {
|
||||
fun doCommentary(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
selectionType: SelectionType,
|
||||
resetCaret: Boolean,
|
||||
resetCaret: Boolean = true,
|
||||
): Boolean {
|
||||
val mode = editor.vimStateMachine.mode
|
||||
if (mode !is Mode.VISUAL) {
|
||||
@@ -67,8 +71,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
}
|
||||
|
||||
return runWriteAction {
|
||||
// Treat block- and character-wise selections as block comments. Be ready to fall back to if the first action
|
||||
// isn't available
|
||||
// Treat block- and character-wise selections as block comments. Fall back if the first action isn't available
|
||||
val actions = if (selectionType === SelectionType.LINE_WISE) {
|
||||
listOf(IdeActions.ACTION_COMMENT_LINE, IdeActions.ACTION_COMMENT_BLOCK)
|
||||
} else {
|
||||
@@ -113,12 +116,17 @@ internal class CommentaryExtension : VimExtension {
|
||||
// first non-whitespace character, then the caret is in the right place. If it's inserted at the first column,
|
||||
// then the caret is now in a bit of a weird place. We can't detect this scenario, so we just have to accept
|
||||
// the difference
|
||||
// TODO: If we don't move the caret to the start offset, we should maintain the current logical position
|
||||
if (resetCaret) {
|
||||
editor.primaryCaret().moveToOffset(range.startOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OPERATOR_FUNC = "CommentaryOperatorFunc"
|
||||
}
|
||||
|
||||
override fun getName() = "commentary"
|
||||
|
||||
override fun init() {
|
||||
@@ -145,6 +153,16 @@ internal class CommentaryExtension : VimExtension {
|
||||
putKeyMapping(MappingMode.N, injector.parser.parseKeys("<Plug>(CommentLine)"), owner, plugCommentaryLineKeys, true)
|
||||
|
||||
addCommand("Commentary", CommentaryCommandAliasHandler())
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, CommentaryOperatorFunction())
|
||||
}
|
||||
|
||||
private class CommentaryOperatorFunction : OperatorFunction {
|
||||
// todo make it multicaret
|
||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
|
||||
return Util.doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,29 +171,23 @@ internal class CommentaryExtension : VimExtension {
|
||||
* E.g. handles the `gc` in `gc_`, by setting the operator function, then invoking `g@` to receive the `_` motion to
|
||||
* invoke the operator. This object is both the mapping handler and the operator function.
|
||||
*/
|
||||
private class CommentaryOperatorHandler : OperatorFunction, ExtensionHandler {
|
||||
private class CommentaryOperatorHandler : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(this)
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
|
||||
// todo make it multicaret
|
||||
override fun apply(editor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val range = injector.markService.getChangeMarks(editor.primaryCaret()) ?: return false
|
||||
return doCommentary(editor, context, range, selectionType ?: SelectionType.CHARACTER_WISE, true)
|
||||
}
|
||||
}
|
||||
|
||||
private class CommentaryMappingHandler : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val commandState = editor.vimStateMachine
|
||||
|
||||
val command = Command(operatorArguments.count1, CommentaryTextObjectMotionHandler, Command.Type.MOTION, EnumSet.noneOf(CommandFlags::class.java))
|
||||
commandState.commandBuilder.completeCommandPart(Argument(command))
|
||||
|
||||
val keyState = KeyHandler.getInstance().keyHandlerState
|
||||
keyState.commandBuilder.completeCommandPart(Argument(command))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,8 +250,14 @@ internal class CommentaryExtension : VimExtension {
|
||||
* Used like `:1,3Commentary` or `g/fun/Commentary`
|
||||
*/
|
||||
private class CommentaryCommandAliasHandler : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
doCommentary(editor, context, ranges.getTextRange(editor, -1), SelectionType.LINE_WISE, false)
|
||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
||||
Util.doCommentary(
|
||||
editor,
|
||||
context,
|
||||
range.getLineRange(editor, editor.primaryCaret()).toTextRange(editor),
|
||||
SelectionType.LINE_WISE,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,24 +19,22 @@ import com.intellij.openapi.util.Key
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
@@ -45,6 +43,8 @@ import com.maddyhome.idea.vim.mark.VimMarkConstants
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
@@ -72,30 +72,13 @@ internal class VimExchangeExtension : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("X"), owner, injector.parser.parseKeys(EXCHANGE_CMD), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxc"), owner, injector.parser.parseKeys(EXCHANGE_CLEAR_CMD), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("cxx"), owner, injector.parser.parseKeys(EXCHANGE_LINE_CMD), true)
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls
|
||||
const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||
|
||||
@NonNls
|
||||
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||
|
||||
@NonNls
|
||||
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||
|
||||
object Util {
|
||||
val EXCHANGE_KEY = Key<Exchange>("exchange")
|
||||
|
||||
// End mark has always greater of eq offset than start mark
|
||||
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
|
||||
private var myHighlighter: RangeHighlighter? = null
|
||||
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||
myHighlighter = highlighter
|
||||
}
|
||||
|
||||
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||
}
|
||||
|
||||
fun clearExchange(editor: Editor) {
|
||||
editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
|
||||
editor.markupModel.removeHighlighter(it)
|
||||
@@ -104,18 +87,25 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls private const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||
@NonNls private const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||
@NonNls private const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||
@NonNls private const val OPERATOR_FUNC = "ExchangeOperatorFunc"
|
||||
}
|
||||
|
||||
private class ExchangeHandler(private val isLine: Boolean) : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator(false))
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys(if (isLine) "g@_" else "g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangeClearHandler : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
clearExchange(editor.ij)
|
||||
Util.clearExchange(editor.ij)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +115,12 @@ internal class VimExchangeExtension : VimExtension {
|
||||
val mode = editor.mode
|
||||
// Leave visual mode to create selection marks
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("<Esc>"), editor.ij)
|
||||
Operator(true).apply(editor, context, mode.selectionType ?: CHARACTER_WISE)
|
||||
Operator(true).apply(editor, context, mode.selectionType ?: SelectionType.CHARACTER_WISE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Operator(private val isVisual: Boolean) : OperatorFunction {
|
||||
private class Operator(private val isVisual: Boolean = false) : OperatorFunction {
|
||||
fun Editor.getMarkOffset(mark: Mark) = IjVimEditor(this).getOffset(mark.line, mark.col)
|
||||
fun SelectionType.getString() = when (this) {
|
||||
SelectionType.CHARACTER_WISE -> "v"
|
||||
@@ -148,7 +138,7 @@ internal class VimExchangeExtension : VimExtension {
|
||||
else -> HighlighterTargetArea.EXACT_RANGE
|
||||
}
|
||||
val isVisualLine = ex.type == SelectionType.LINE_WISE
|
||||
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || (isVisual))) 1 else 0
|
||||
val endAdj = if (!(isVisualLine) && (hlArea == HighlighterTargetArea.EXACT_RANGE || isVisual)) 1 else 0
|
||||
return ijEditor.markupModel.addRangeHighlighter(
|
||||
ijEditor.getMarkOffset(ex.start),
|
||||
(ijEditor.getMarkOffset(ex.end) + endAdj).coerceAtMost(ijEditor.fileSize),
|
||||
@@ -158,12 +148,12 @@ internal class VimExchangeExtension : VimExtension {
|
||||
)
|
||||
}
|
||||
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(EXCHANGE_KEY)
|
||||
val currentExchange = getExchange(ijEditor, isVisual, selectionType ?: SelectionType.CHARACTER_WISE)
|
||||
val exchange1 = ijEditor.getUserData(Util.EXCHANGE_KEY)
|
||||
if (exchange1 == null) {
|
||||
val highlighter = highlightExchange(currentExchange)
|
||||
currentExchange.setHighlighter(highlighter)
|
||||
ijEditor.putUserData(EXCHANGE_KEY, currentExchange)
|
||||
ijEditor.putUserData(Util.EXCHANGE_KEY, currentExchange)
|
||||
return true
|
||||
} else {
|
||||
val cmp = compareExchanges(exchange1, currentExchange)
|
||||
@@ -189,7 +179,7 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
exchange(ijEditor, ex1, ex2, reverse, expand)
|
||||
clearExchange(ijEditor)
|
||||
Util.clearExchange(ijEditor)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -354,4 +344,14 @@ internal class VimExchangeExtension : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End mark has always greater of eq offset than start mark
|
||||
class Exchange(val type: SelectionType, val start: Mark, val end: Mark, val text: String) {
|
||||
private var myHighlighter: RangeHighlighter? = null
|
||||
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||
myHighlighter = highlighter
|
||||
}
|
||||
|
||||
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ package com.maddyhome.idea.vim.extension.highlightedyank
|
||||
|
||||
import com.intellij.ide.ui.LafManager
|
||||
import com.intellij.ide.ui.LafManagerListener
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.markup.EffectType
|
||||
@@ -19,25 +19,24 @@ import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.util.Alarm
|
||||
import com.intellij.util.Alarm.ThreadToUse
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.VimProjectService
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.StrictMode
|
||||
import com.maddyhome.idea.vim.helper.VimNlsSafe
|
||||
import com.maddyhome.idea.vim.listener.VimInsertListener
|
||||
import com.maddyhome.idea.vim.listener.VimYankListener
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
internal const val DEFAULT_HIGHLIGHT_DURATION: Long = 300
|
||||
internal const val DEFAULT_HIGHLIGHT_DURATION: Int = 300
|
||||
|
||||
@NonNls
|
||||
private val HIGHLIGHT_DURATION_VARIABLE_NAME = "highlightedyank_highlight_duration"
|
||||
@@ -78,111 +77,128 @@ internal class HighlightColorResetter : LafManagerListener {
|
||||
*/
|
||||
internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertListener {
|
||||
private val highlightHandler = HighlightHandler()
|
||||
private var initialised = false
|
||||
|
||||
override fun getName() = "highlightedyank"
|
||||
|
||||
override fun init() {
|
||||
// Note that these listeners will still be registered when IdeaVim is disabled. However, they'll never get called
|
||||
VimPlugin.getYank().addListener(this)
|
||||
VimPlugin.getChange().addInsertListener(this)
|
||||
|
||||
// Register our own disposable to remove highlights when IdeaVim is disabled. Somehow, we need to re-register when
|
||||
// IdeaVim is re-enabled. We don't get a call back for that, but because the listeners are active until the
|
||||
// _extension_ is disabled, make sure we're properly initialised each time we're called.
|
||||
registerIdeaVimDisabledCallback()
|
||||
initialised = true
|
||||
}
|
||||
|
||||
private fun registerIdeaVimDisabledCallback() {
|
||||
// TODO: IdeaVim should help with lifecycle management here - VIM-3419
|
||||
// IdeaVim should possibly unregister extensions, but it would also need to re-register them. We have to do this
|
||||
// state management manually for now
|
||||
Disposer.register(VimPlugin.getInstance().onOffDisposable) {
|
||||
highlightHandler.clearYankHighlighters()
|
||||
initialised = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
// Called when the extension is disabled with `:set no{extension}` or if the plugin owning the extension unloads
|
||||
VimPlugin.getYank().removeListener(this)
|
||||
VimPlugin.getChange().removeInsertListener(this)
|
||||
|
||||
highlightHandler.clearYankHighlighters()
|
||||
initialised = false
|
||||
}
|
||||
|
||||
override fun yankPerformed(editor: VimEditor, range: TextRange) {
|
||||
ensureInitialised()
|
||||
highlightHandler.highlightYankRange(editor.ij, range)
|
||||
}
|
||||
|
||||
override fun insertModeStarted(editor: Editor) {
|
||||
highlightHandler.clearAllYankHighlighters()
|
||||
ensureInitialised()
|
||||
highlightHandler.clearYankHighlighters()
|
||||
}
|
||||
|
||||
private fun ensureInitialised() {
|
||||
if (!initialised) {
|
||||
registerIdeaVimDisabledCallback()
|
||||
initialised = true
|
||||
}
|
||||
}
|
||||
|
||||
private class HighlightHandler {
|
||||
private var editor: Editor? = null
|
||||
private val yankHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
||||
private val alarm = Alarm(ThreadToUse.SWING_THREAD)
|
||||
private var lastEditor: Editor? = null
|
||||
private val highlighters = mutableSetOf<RangeHighlighter>()
|
||||
|
||||
fun highlightYankRange(editor: Editor, range: TextRange) {
|
||||
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
|
||||
clearAllYankHighlighters()
|
||||
clearYankHighlighters()
|
||||
|
||||
this.editor = editor
|
||||
val project = editor.project
|
||||
if (project != null) {
|
||||
Disposer.register(
|
||||
VimProjectService.getInstance(project),
|
||||
) {
|
||||
this.editor = null
|
||||
yankHighlighters.clear()
|
||||
}
|
||||
lastEditor = editor
|
||||
|
||||
val attributes = getHighlightTextAttributes(editor)
|
||||
for (i in 0 until range.size()) {
|
||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||
range.startOffsets[i],
|
||||
range.endOffsets[i],
|
||||
HighlighterLayer.SELECTION,
|
||||
attributes,
|
||||
HighlighterTargetArea.EXACT_RANGE,
|
||||
)
|
||||
highlighters.add(highlighter)
|
||||
}
|
||||
|
||||
if (range.isMultiple) {
|
||||
for (i in 0 until range.size()) {
|
||||
highlightSingleRange(editor, range.startOffsets[i]..range.endOffsets[i])
|
||||
}
|
||||
} else {
|
||||
highlightSingleRange(editor, range.startOffset..range.endOffset)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearAllYankHighlighters() {
|
||||
yankHighlighters.forEach { highlighter ->
|
||||
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||
}
|
||||
|
||||
yankHighlighters.clear()
|
||||
}
|
||||
|
||||
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
|
||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||
range.start,
|
||||
range.endInclusive,
|
||||
HighlighterLayer.SELECTION,
|
||||
getHighlightTextAttributes(),
|
||||
HighlighterTargetArea.EXACT_RANGE,
|
||||
)
|
||||
|
||||
yankHighlighters.add(highlighter)
|
||||
|
||||
setClearHighlightRangeTimer(highlighter)
|
||||
}
|
||||
|
||||
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
||||
val timeout = extractUsersHighlightDuration()
|
||||
|
||||
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
|
||||
val timeout = extractUsersHighlightDuration()
|
||||
if (timeout >= 0) {
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(
|
||||
{
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||
}
|
||||
},
|
||||
// Note modality. This is important when highlighting an editor when a modal dialog is open, such as the resolve
|
||||
// conflict diff view
|
||||
alarm.addRequest(
|
||||
{ clearYankHighlighters() },
|
||||
timeout,
|
||||
TimeUnit.MILLISECONDS,
|
||||
ModalityState.any()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHighlightTextAttributes() = TextAttributes(
|
||||
fun clearYankHighlighters() {
|
||||
alarm.cancelAllRequests()
|
||||
// Make sure the last editor we saved is still alive before we use it. We can't just use the list of open editors
|
||||
// because this list is empty when IdeaVim is disabled, so we're unable to clean up
|
||||
lastEditor?.let { editor ->
|
||||
if (!editor.isDisposed) {
|
||||
highlighters.forEach { highlighter -> editor.markupModel.removeHighlighter(highlighter) }
|
||||
}
|
||||
}
|
||||
lastEditor = null
|
||||
highlighters.clear()
|
||||
}
|
||||
|
||||
private fun getHighlightTextAttributes(editor: Editor) = TextAttributes(
|
||||
null,
|
||||
extractUsersHighlightColor(),
|
||||
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
|
||||
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
|
||||
EffectType.SEARCH_MATCH,
|
||||
Font.PLAIN,
|
||||
)
|
||||
|
||||
private fun extractUsersHighlightDuration(): Long {
|
||||
private fun extractUsersHighlightDuration(): Int {
|
||||
return extractVariable(HIGHLIGHT_DURATION_VARIABLE_NAME, DEFAULT_HIGHLIGHT_DURATION) {
|
||||
it.toLong()
|
||||
// toVimNumber will return 0 for an invalid string. Let's force it to throw
|
||||
when (it) {
|
||||
is VimString -> it.value.toInt()
|
||||
else -> it.toVimNumber().value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractUsersHighlightColor(): Color {
|
||||
return extractVariable(HIGHLIGHT_COLOR_VARIABLE_NAME, getDefaultHighlightTextColor()) { value ->
|
||||
val rgba = value
|
||||
val rgba = value.asString()
|
||||
.substring(4)
|
||||
.filter { it != '(' && it != ')' && !it.isWhitespace() }
|
||||
.split(',')
|
||||
@@ -192,12 +208,11 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, VimInsertList
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: String) -> T): T {
|
||||
private fun <T> extractVariable(variable: String, default: T, extractFun: (value: VimDataType) -> T): T {
|
||||
val value = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||
|
||||
if (value is VimString) {
|
||||
if (value != null) {
|
||||
return try {
|
||||
extractFun(value.value)
|
||||
extractFun(value)
|
||||
} catch (e: Exception) {
|
||||
@VimNlsSafe val message = MessageHelper.message(
|
||||
"highlightedyank.invalid.value.of.0.1",
|
||||
|
@@ -14,6 +14,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
@@ -40,7 +41,6 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
@@ -91,22 +91,23 @@ internal class Matchit : VimExtension {
|
||||
private class MatchitHandler(private val reverse: Boolean) : ExtensionHandler {
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val commandState = editor.vimStateMachine
|
||||
val count = commandState.commandBuilder.count
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
val keyState = keyHandler.keyHandlerState
|
||||
val count = keyState.commandBuilder.count
|
||||
|
||||
// Reset the command count so it doesn't transfer onto subsequent commands.
|
||||
editor.vimStateMachine.commandBuilder.resetCount()
|
||||
keyState.commandBuilder.resetCount()
|
||||
|
||||
// Normally we want to jump to the start of the matching pair. But when moving forward in operator
|
||||
// pending mode, we want to include the entire match. isInOpPending makes that distinction.
|
||||
val isInOpPending = commandState.isOperatorPending
|
||||
val isInOpPending = keyHandler.isOperatorPending(editor.mode, keyState)
|
||||
|
||||
if (isInOpPending) {
|
||||
val matchitAction = MatchitAction()
|
||||
matchitAction.reverse = reverse
|
||||
matchitAction.isInOpPending = true
|
||||
|
||||
commandState.commandBuilder.completeCommandPart(
|
||||
keyState.commandBuilder.completeCommandPart(
|
||||
Argument(
|
||||
Command(
|
||||
count,
|
||||
|
@@ -30,11 +30,11 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMa
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchOptions
|
||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.findWordUnderCursor
|
||||
import com.maddyhome.idea.vim.helper.inVisualMode
|
||||
import com.maddyhome.idea.vim.helper.updateCaretsVisualAttributes
|
||||
import com.maddyhome.idea.vim.helper.userData
|
||||
@@ -235,7 +235,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
val text = if (editor.inVisualMode) {
|
||||
primaryCaret.selectedText ?: return
|
||||
} else {
|
||||
val range = SearchHelper.findWordUnderCursor(editor, primaryCaret) ?: return
|
||||
val range = findWordUnderCursor(editor, primaryCaret) ?: return
|
||||
if (range.startOffset > primaryCaret.offset) return
|
||||
IjVimEditor(editor).getText(range)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
}
|
||||
|
||||
private fun selectWordUnderCaret(editor: Editor, caret: Caret): TextRange? {
|
||||
val range = SearchHelper.findWordUnderCursor(editor, caret) ?: return null
|
||||
val range = findWordUnderCursor(editor, caret) ?: return null
|
||||
if (range.startOffset > caret.offset) return null
|
||||
|
||||
enterVisualMode(editor.vim)
|
||||
@@ -327,6 +327,6 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
|
||||
private fun makePattern(text: String, whole: Boolean): String {
|
||||
// Pattern is "very nomagic" (ignore regex chars) and "force case sensitive". This is vim-multiple-cursors behaviour
|
||||
return "\\V\\C" + SearchHelper.makeSearchPattern(text, whole)
|
||||
return "\\V\\C" + if (whole) "\\<$text\\>" else text
|
||||
}
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.CommandAlias
|
||||
import com.maddyhome.idea.vim.common.CommandAliasHandler
|
||||
import com.maddyhome.idea.vim.ex.ranges.Ranges
|
||||
import com.maddyhome.idea.vim.ex.ranges.Range
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.group.KeyGroup
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||
@@ -130,32 +130,32 @@ internal class NerdTree : VimExtension {
|
||||
addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
|
||||
addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
|
||||
|
||||
synchronized(monitor) {
|
||||
commandsRegistered = true
|
||||
synchronized(Util.monitor) {
|
||||
Util.commandsRegistered = true
|
||||
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
|
||||
}
|
||||
}
|
||||
|
||||
class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
callAction(editor, actionId, context)
|
||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
||||
Util.callAction(editor, actionId, context)
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleHandler : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
||||
val project = editor.ij.project ?: return
|
||||
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
||||
if (toolWindow.isVisible) {
|
||||
toolWindow.hide()
|
||||
} else {
|
||||
callAction(editor, "ActivateProjectToolWindow", context)
|
||||
Util.callAction(editor, "ActivateProjectToolWindow", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CloseHandler : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
override fun execute(command: String, range: Range, editor: VimEditor, context: ExecutionContext) {
|
||||
val project = editor.ij.project ?: return
|
||||
val toolWindow = ToolWindowManagerEx.getInstanceEx(project).getToolWindow(ToolWindowId.PROJECT_VIEW) ?: return
|
||||
if (toolWindow.isVisible) {
|
||||
@@ -187,8 +187,8 @@ internal class NerdTree : VimExtension {
|
||||
// TODO I'm not sure is this activity runs at all? Should we use [RunOnceUtil] instead?
|
||||
class NerdStartupActivity : ProjectActivity {
|
||||
override suspend fun execute(project: Project) {
|
||||
synchronized(monitor) {
|
||||
if (!commandsRegistered) return
|
||||
synchronized(Util.monitor) {
|
||||
if (!Util.commandsRegistered) return
|
||||
installDispatcher(project)
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
val action = nextNode.actionHolder
|
||||
when (action) {
|
||||
is NerdAction.ToIj -> 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) }
|
||||
}
|
||||
}
|
||||
@@ -356,7 +356,7 @@ internal class NerdTree : VimExtension {
|
||||
currentWindow?.split(SwingConstants.VERTICAL, true, file, true)
|
||||
|
||||
// FIXME: 22.01.2021 This solution bouncing a bit
|
||||
callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
},
|
||||
)
|
||||
registerCommand(
|
||||
@@ -368,7 +368,7 @@ internal class NerdTree : VimExtension {
|
||||
val currentWindow = splitters.currentWindow
|
||||
currentWindow?.split(SwingConstants.HORIZONTAL, true, file, true)
|
||||
|
||||
callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
Util.callAction(null, "ActivateProjectToolWindow", context.vim)
|
||||
},
|
||||
)
|
||||
registerCommand(
|
||||
@@ -477,6 +477,10 @@ internal class NerdTree : VimExtension {
|
||||
"r",
|
||||
NerdAction.ToIj("SynchronizeCurrentFile"),
|
||||
)
|
||||
registerCommand("NERDTreeMapToggleHidden", "I", NerdAction.ToIj("ProjectView.ShowExcludedFiles"))
|
||||
registerCommand("NERDTreeMapNewFile", "n", NerdAction.ToIj("NewFile"))
|
||||
registerCommand("NERDTreeMapNewDir", "N", NerdAction.ToIj("NewDir"))
|
||||
registerCommand("NERDTreeMapDelete", "d", NerdAction.ToIj("\$Delete"))
|
||||
registerCommand("NERDTreeMapRefreshRoot", "R", NerdAction.ToIj("Synchronize"))
|
||||
registerCommand("NERDTreeMapMenu", "m", NerdAction.ToIj("ShowPopupMenu"))
|
||||
registerCommand("NERDTreeMapQuit", "q", NerdAction.ToIj("HideActiveWindow"))
|
||||
@@ -502,23 +506,11 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
for (c in ('a'..'z') + ('A'..'Z')) {
|
||||
val ks = KeyStroke.getKeyStroke(c)
|
||||
if (ks !in actionsRoot) {
|
||||
registerCommand(c.toString(), NerdAction.Code { _, _, _ -> })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
|
||||
object Util {
|
||||
internal val monitor = Any()
|
||||
internal var commandsRegistered = false
|
||||
|
||||
private val LOG = logger<NerdTree>()
|
||||
|
||||
fun callAction(editor: VimEditor?, name: String, context: ExecutionContext) {
|
||||
val action = ActionManager.getInstance().getAction(name) ?: run {
|
||||
VimPlugin.showMessage(MessageHelper.message("action.not.found.0", name))
|
||||
@@ -533,45 +525,51 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||
}
|
||||
|
||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
|
||||
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||
val mappings = if (variableValue is VimString) {
|
||||
variableValue.value
|
||||
} else {
|
||||
default
|
||||
}
|
||||
actionsRoot.addLeafs(mappings, action)
|
||||
}
|
||||
|
||||
private fun registerCommand(default: String, action: NerdAction) {
|
||||
actionsRoot.addLeafs(default, action)
|
||||
}
|
||||
|
||||
private val actionsRoot: RootNode<NerdAction> = RootNode()
|
||||
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) {
|
||||
val dispatcher = NerdDispatcher.getInstance(project)
|
||||
val shortcuts = collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(pluginName)) }
|
||||
dispatcher.registerCustomShortcutSet(
|
||||
KeyGroup.toShortcutSet(shortcuts),
|
||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
||||
)
|
||||
}
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
private val LOG = logger<NerdTree>()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||
}
|
||||
|
||||
private fun registerCommand(variable: String, default: String, action: NerdAction) {
|
||||
val variableValue = VimPlugin.getVariableService().getGlobalVariableValue(variable)
|
||||
val mappings = if (variableValue is VimString) {
|
||||
variableValue.value
|
||||
} else {
|
||||
default
|
||||
}
|
||||
actionsRoot.addLeafs(mappings, action)
|
||||
}
|
||||
|
||||
private fun registerCommand(default: String, action: NerdAction) {
|
||||
actionsRoot.addLeafs(default, action)
|
||||
}
|
||||
|
||||
|
||||
private val actionsRoot: RootNode<NerdAction> = RootNode()
|
||||
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) {
|
||||
val dispatcher = NerdTree.NerdDispatcher.getInstance(project)
|
||||
val shortcuts =
|
||||
collectShortcuts(actionsRoot).map { RequiredShortcut(it, MappingOwner.Plugin.get(NerdTree.pluginName)) }
|
||||
dispatcher.registerCustomShortcutSet(
|
||||
KeyGroup.toShortcutSet(shortcuts),
|
||||
(ProjectView.getInstance(project) as ProjectViewImpl).component,
|
||||
)
|
||||
}
|
||||
|
@@ -9,10 +9,11 @@
|
||||
package com.maddyhome.idea.vim.extension.paragraphmotion
|
||||
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndForOffset
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
@@ -20,8 +21,10 @@ import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.helper.vimForEachCaret
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
internal class ParagraphMotion : VimExtension {
|
||||
override fun getName(): String = "vim-paragraph-motion"
|
||||
@@ -30,8 +33,8 @@ internal class ParagraphMotion : VimExtension {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), owner, ParagraphMotionHandler(1), false)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.NXO, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), owner, ParagraphMotionHandler(-1), false)
|
||||
|
||||
putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
|
||||
putKeyMappingIfMissing(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
|
||||
putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("}"), owner, injector.parser.parseKeys("<Plug>(ParagraphNextMotion)"), true)
|
||||
putKeyMappingIfMissingFromAndToKeys(MappingMode.NXO, injector.parser.parseKeys("{"), owner, injector.parser.parseKeys("<Plug>(ParagraphPrevMotion)"), true)
|
||||
}
|
||||
|
||||
private class ParagraphMotionHandler(private val count: Int) : ExtensionHandler {
|
||||
@@ -45,7 +48,21 @@ internal class ParagraphMotion : VimExtension {
|
||||
}
|
||||
|
||||
fun moveCaretToNextParagraph(editor: VimEditor, caret: Caret, count: Int): Int? {
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)?.let(editor::getLineEndForOffset)
|
||||
return injector.searchHelper.findNextParagraph(editor, caret.vim, count, true)
|
||||
?.let { editor.normalizeOffset(it, true) }
|
||||
}
|
||||
}
|
||||
|
||||
// For VIM-3306
|
||||
@Suppress("SameParameterValue")
|
||||
private fun putKeyMappingIfMissingFromAndToKeys(
|
||||
modes: Set<MappingMode>,
|
||||
fromKeys: List<KeyStroke>,
|
||||
pluginOwner: MappingOwner,
|
||||
toKeys: List<KeyStroke>,
|
||||
recursive: Boolean,
|
||||
) {
|
||||
val filteredModes = modes.filterNotTo(HashSet()) { VimPlugin.getKey().hasmapfrom(it, fromKeys) }
|
||||
putKeyMappingIfMissing(filteredModes, fromKeys, pluginOwner, toKeys, recursive)
|
||||
}
|
||||
}
|
||||
|
@@ -8,37 +8,37 @@
|
||||
|
||||
package com.maddyhome.idea.vim.extension.replacewithregister
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.getLineEndOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType.CHARACTER_WISE
|
||||
import com.maddyhome.idea.vim.state.mode.isLine
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
||||
import com.maddyhome.idea.vim.put.PutData
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.isLine
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
internal class ReplaceWithRegister : VimExtension {
|
||||
@@ -53,17 +53,19 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_OPERATOR), true)
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("grr"), owner, injector.parser.parseKeys(RWR_LINE), true)
|
||||
putKeyMappingIfMissing(MappingMode.X, injector.parser.parseKeys("gr"), owner, injector.parser.parseKeys(RWR_VISUAL), true)
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator())
|
||||
}
|
||||
|
||||
private class RwrVisual : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val typeInEditor = editor.mode.selectionType ?: CHARACTER_WISE
|
||||
val typeInEditor = editor.mode.selectionType ?: SelectionType.CHARACTER_WISE
|
||||
editor.sortedCarets().forEach { caret ->
|
||||
val selectionStart = caret.selectionStart
|
||||
val selectionEnd = caret.selectionEnd
|
||||
|
||||
val visualSelection = caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), typeInEditor))
|
||||
}
|
||||
editor.exitVisualMode()
|
||||
}
|
||||
@@ -73,7 +75,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
override val isRepeatable: Boolean = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator())
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@@ -91,7 +93,7 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
val visualSelection = caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
||||
caretsAndSelections += visualSelection
|
||||
|
||||
doReplace(editor.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||
doReplace(editor.ij, context.ij, caret, PutData.VisualSelection(mapOf(visualSelection), SelectionType.LINE_WISE))
|
||||
}
|
||||
|
||||
editor.sortedCarets().forEach { caret ->
|
||||
@@ -112,14 +114,14 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
editor.primaryCaret() to VimSelection.create(
|
||||
range.startOffset,
|
||||
range.endOffset - 1,
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
editor,
|
||||
),
|
||||
),
|
||||
selectionType ?: CHARACTER_WISE,
|
||||
selectionType ?: SelectionType.CHARACTER_WISE,
|
||||
)
|
||||
// todo multicaret
|
||||
doReplace(ijEditor, editor.primaryCaret(), visualSelection)
|
||||
doReplace(ijEditor, context.ij, editor.primaryCaret(), visualSelection)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -132,52 +134,50 @@ internal class ReplaceWithRegister : VimExtension {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@NonNls
|
||||
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||
|
||||
@NonNls
|
||||
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||
|
||||
@NonNls
|
||||
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||
|
||||
private fun doReplace(editor: Editor, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||
val registerGroup = injector.registerGroup
|
||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||
val savedRegister = caret.registerStorage.getRegister(lastRegisterChar) ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||
// Code from original plugin implementation. Correct text for linewise selected text
|
||||
usedText = usedText.dropLast(1)
|
||||
usedType = SelectionType.CHARACTER_WISE
|
||||
}
|
||||
|
||||
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
|
||||
|
||||
val putData = PutData(
|
||||
textData,
|
||||
visualSelection,
|
||||
1,
|
||||
insertTextBeforeCaret = true,
|
||||
rawIndent = true,
|
||||
caretAfterInsertedText = false,
|
||||
putToLine = -1,
|
||||
)
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
IjVimEditor(editor),
|
||||
injector.executionContextManager.onEditor(editor.vim),
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending ?: false,
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
||||
@NonNls private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||
@NonNls private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||
@NonNls private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||
@NonNls private const val OPERATOR_FUNC = "ReplaceWithRegisterOperatorFunc"
|
||||
}
|
||||
}
|
||||
|
||||
private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimCaret, visualSelection: PutData.VisualSelection) {
|
||||
val registerGroup = injector.registerGroup
|
||||
val lastRegisterChar = if (editor.caretModel.caretCount == 1) registerGroup.currentRegister else registerGroup.getCurrentRegisterForMulticaret()
|
||||
val savedRegister = registerGroup.getRegister(lastRegisterChar) ?: return
|
||||
|
||||
var usedType = savedRegister.type
|
||||
var usedText = savedRegister.text
|
||||
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||
// Code from original plugin implementation. Correct text for linewise selected text
|
||||
usedText = usedText.dropLast(1)
|
||||
usedType = SelectionType.CHARACTER_WISE
|
||||
}
|
||||
|
||||
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
|
||||
|
||||
val putData = PutData(
|
||||
textData,
|
||||
visualSelection,
|
||||
1,
|
||||
insertTextBeforeCaret = true,
|
||||
rawIndent = true,
|
||||
caretAfterInsertedText = false,
|
||||
putToLine = -1,
|
||||
)
|
||||
val vimEditor = editor.vim
|
||||
val keyHandler = KeyHandler.getInstance()
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
vimEditor,
|
||||
context.vim,
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
keyHandler.isOperatorPending(vimEditor.mode, keyHandler.keyHandlerState),
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -36,6 +36,7 @@ import com.maddyhome.idea.vim.helper.StrictMode
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import java.awt.Font
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.*
|
||||
import javax.swing.Timer
|
||||
|
||||
|
||||
@@ -48,17 +49,20 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
|
||||
override fun init() {
|
||||
val highlightHandler = HighlightHandler()
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD))
|
||||
mapToFunctionAndProvideKeys("S", SneakHandler(highlightHandler, Direction.BACKWARD))
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
|
||||
|
||||
// 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("Z", SneakHandler(highlightHandler, Direction.BACKWARD), MappingMode.X)
|
||||
|
||||
// workaround to support ; and , commands
|
||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"))
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"))
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"))
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"))
|
||||
mapToFunctionAndProvideKeys("f", SneakMemoryHandler("f"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("F", SneakMemoryHandler("F"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("t", SneakMemoryHandler("t"), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys("T", SneakMemoryHandler("T"), MappingMode.NXO)
|
||||
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL))
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE))
|
||||
mapToFunctionAndProvideKeys(";", SneakRepeatHandler(highlightHandler, RepeatDirection.IDENTICAL), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
||||
}
|
||||
|
||||
private class SneakHandler(
|
||||
@@ -114,7 +118,7 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
var lastSymbols: String = ""
|
||||
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
||||
val caret = editor.primaryCaret()
|
||||
val position = caret.offset.point
|
||||
val position = caret.offset
|
||||
val chars = editor.text()
|
||||
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
||||
if (foundPosition != null) {
|
||||
@@ -273,16 +277,18 @@ internal class IdeaVimSneakExtension : VimExtension {
|
||||
* Map some <Plug>(keys) command to given handler
|
||||
* and create mapping to <Plug>(prefix)[keys]
|
||||
*/
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: ExtensionHandler) {
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(
|
||||
keys: String, handler: ExtensionHandler, mappingModes: EnumSet<MappingMode>
|
||||
) {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
MappingMode.NXO,
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
MappingMode.NXO,
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
|
||||
owner,
|
||||
handler,
|
||||
@@ -294,17 +300,17 @@ private fun VimExtension.mapToFunctionAndProvideKeys(keys: String, handler: Exte
|
||||
// - The shortcut should not be registered if any of these mappings is overridden in .ideavimrc
|
||||
// - The shortcut should not be registered if some other shortcut for this key exists
|
||||
val fromKeys = injector.parser.parseKeys(keys)
|
||||
val filteredModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredModes = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
|
||||
}
|
||||
val filteredModes2 = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredModes2 = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
||||
}
|
||||
val filteredFromModes = MappingMode.NXO.filterNotTo(HashSet()) {
|
||||
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
||||
injector.keyGroup.hasmapfrom(it, fromKeys)
|
||||
}
|
||||
|
||||
val doubleFiltered = MappingMode.NXO
|
||||
val doubleFiltered = mappingModes
|
||||
.filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
|
||||
.toSet()
|
||||
putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.extension.surround
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
@@ -17,6 +18,7 @@ import com.maddyhome.idea.vim.api.VimChangeGroup
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.endsWithNewLine
|
||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||
import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.setChangeMarks
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
@@ -24,14 +26,16 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.extension.ExtensionHandler
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormalWithoutMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegisterForCaret
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputString
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMappingIfMissing
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegisterForCaret
|
||||
import com.maddyhome.idea.vim.extension.exportOperatorFunction
|
||||
import com.maddyhome.idea.vim.group.findBlockRange
|
||||
import com.maddyhome.idea.vim.helper.runWithEveryCaretAndRestore
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
@@ -42,7 +46,6 @@ import com.maddyhome.idea.vim.options.helpers.ClipboardOptionHelper
|
||||
import com.maddyhome.idea.vim.put.PutData
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import java.awt.event.KeyEvent
|
||||
@@ -78,13 +81,15 @@ internal class VimSurroundExtension : VimExtension {
|
||||
putKeyMappingIfMissing(MappingMode.N, injector.parser.parseKeys("ds"), owner, injector.parser.parseKeys("<Plug>DSurround"), true)
|
||||
putKeyMappingIfMissing(MappingMode.XO, injector.parser.parseKeys("S"), owner, injector.parser.parseKeys("<Plug>VSurround"), true)
|
||||
}
|
||||
|
||||
VimExtensionFacade.exportOperatorFunction(OPERATOR_FUNC, Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||
}
|
||||
|
||||
private class YSurroundHandler : ExtensionHandler {
|
||||
override val isRepeatable = true
|
||||
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
setOperatorFunction(Operator(supportsMultipleCursors = false, count = 1)) // TODO
|
||||
injector.globalOptions().operatorfunc = OPERATOR_FUNC
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("g@"), editor.ij)
|
||||
}
|
||||
}
|
||||
@@ -96,7 +101,7 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val ijEditor = editor.ij
|
||||
val c = getChar(ijEditor)
|
||||
if (c.code == 0) return
|
||||
val pair = getOrInputPair(c, ijEditor) ?: return
|
||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return
|
||||
|
||||
editor.forEachCaret {
|
||||
val line = it.getBufferPosition().line
|
||||
@@ -146,7 +151,7 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val charTo = getChar(editor.ij)
|
||||
if (charTo.code == 0) return
|
||||
|
||||
val newSurround = getOrInputPair(charTo, editor.ij) ?: return
|
||||
val newSurround = getOrInputPair(charTo, editor.ij, context.ij) ?: return
|
||||
runWriteAction { change(editor, context, charFrom, newSurround) }
|
||||
}
|
||||
|
||||
@@ -223,12 +228,12 @@ internal class VimSurroundExtension : VimExtension {
|
||||
val searchHelper = injector.searchHelper
|
||||
return when (char) {
|
||||
't' -> searchHelper.findBlockTagRange(editor, caret, 1, true)
|
||||
'(', ')', 'b' -> searchHelper.findBlockRange(editor, caret, '(', 1, true)
|
||||
'[', ']' -> searchHelper.findBlockRange(editor, caret, '[', 1, true)
|
||||
'{', '}', 'B' -> searchHelper.findBlockRange(editor, caret, '{', 1, true)
|
||||
'<', '>' -> searchHelper.findBlockRange(editor, caret, '<', 1, true)
|
||||
'(', ')', 'b' -> findBlockRange(editor, caret, '(', 1, true)
|
||||
'[', ']' -> findBlockRange(editor, caret, '[', 1, true)
|
||||
'{', '}', 'B' -> findBlockRange(editor, caret, '{', 1, true)
|
||||
'<', '>' -> findBlockRange(editor, caret, '<', 1, true)
|
||||
'`', '\'', '"' -> {
|
||||
val caretOffset = caret.offset.point
|
||||
val caretOffset = caret.offset
|
||||
val text = editor.text()
|
||||
if (text.getOrNull(caretOffset - 1) == char && text.getOrNull(caretOffset) == char) {
|
||||
TextRange(caretOffset - 1, caretOffset + 1)
|
||||
@@ -265,23 +270,23 @@ internal class VimSurroundExtension : VimExtension {
|
||||
|
||||
private class Operator(private val supportsMultipleCursors: Boolean, private val count: Int) : OperatorFunction {
|
||||
override fun apply(vimEditor: VimEditor, context: ExecutionContext, selectionType: SelectionType?): Boolean {
|
||||
val editor = vimEditor.ij
|
||||
val c = getChar(editor)
|
||||
val ijEditor = vimEditor.ij
|
||||
val c = getChar(ijEditor)
|
||||
if (c.code == 0) return true
|
||||
|
||||
val pair = getOrInputPair(c, editor) ?: return false
|
||||
val pair = getOrInputPair(c, ijEditor, context.ij) ?: return false
|
||||
|
||||
runWriteAction {
|
||||
val change = VimPlugin.getChange()
|
||||
if (supportsMultipleCursors) {
|
||||
editor.runWithEveryCaretAndRestore {
|
||||
applyOnce(editor, change, pair, count)
|
||||
ijEditor.runWithEveryCaretAndRestore {
|
||||
applyOnce(ijEditor, change, pair, count)
|
||||
}
|
||||
}
|
||||
else {
|
||||
applyOnce(editor, change, pair, count)
|
||||
applyOnce(ijEditor, change, pair, count)
|
||||
// Jump back to start
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), editor)
|
||||
executeNormalWithoutMapping(injector.parser.parseKeys("`["), ijEditor)
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -315,7 +320,9 @@ private val LOG = logger<VimSurroundExtension>()
|
||||
|
||||
private const val REGISTER = '"'
|
||||
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
private const val OPERATOR_FUNC = "SurroundOperatorFunc"
|
||||
|
||||
private val tagNameAndAttributesCapturePattern = "(\\S+)([^>]*)>".toPattern()
|
||||
|
||||
private val SURROUND_PAIRS = mapOf(
|
||||
'b' to ("(" to ")"),
|
||||
@@ -341,8 +348,8 @@ private fun getSurroundPair(c: Char): Pair<String, String>? = if (c in SURROUND_
|
||||
null
|
||||
}
|
||||
|
||||
private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
||||
val tagInput = inputString(editor, "<", '>')
|
||||
private fun inputTagPair(editor: Editor, context: DataContext): Pair<String, String>? {
|
||||
val tagInput = inputString(editor, context, "<", '>')
|
||||
val matcher = tagNameAndAttributesCapturePattern.matcher(tagInput)
|
||||
return if (matcher.find()) {
|
||||
val tagName = matcher.group(1)
|
||||
@@ -355,17 +362,18 @@ private fun inputTagPair(editor: Editor): Pair<String, String>? {
|
||||
|
||||
private fun inputFunctionName(
|
||||
editor: Editor,
|
||||
context: DataContext,
|
||||
withInternalSpaces: Boolean,
|
||||
): Pair<String, String>? {
|
||||
val functionNameInput = inputString(editor, "function: ", null)
|
||||
val functionNameInput = inputString(editor, context, "function: ", null)
|
||||
if (functionNameInput.isEmpty()) return null
|
||||
return if (withInternalSpaces) "$functionNameInput( " to " )" else "$functionNameInput(" to ")"
|
||||
}
|
||||
|
||||
private fun getOrInputPair(c: Char, editor: Editor): Pair<String, String>? = when (c) {
|
||||
'<', 't' -> inputTagPair(editor)
|
||||
'f' -> inputFunctionName(editor, false)
|
||||
'F' -> inputFunctionName(editor, true)
|
||||
private fun getOrInputPair(c: Char, editor: Editor, context: DataContext): Pair<String, String>? = when (c) {
|
||||
'<', 't' -> inputTagPair(editor, context)
|
||||
'f' -> inputFunctionName(editor, context, false)
|
||||
'F' -> inputFunctionName(editor, context, true)
|
||||
else -> getSurroundPair(c)
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.textobjentire;
|
||||
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
@@ -23,7 +24,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -133,17 +134,18 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(editor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
com.maddyhome.idea.vim.group.visual.EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||
@@ -153,7 +155,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION,
|
||||
EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
package com.maddyhome.idea.vim.extension.textobjindent;
|
||||
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext;
|
||||
import com.maddyhome.idea.vim.api.ImmutableVimCaret;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
@@ -24,7 +25,7 @@ import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.KeyHandlerState;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -131,10 +132,11 @@ public class VimIndentObject implements VimExtension {
|
||||
// This is done as a separate step so that it works even when the caret is inside the indentation.
|
||||
int offset = caretLineStartOffset;
|
||||
int indentSize = 0;
|
||||
while (++offset < charSequence.length()) {
|
||||
while (offset < charSequence.length()) {
|
||||
final char ch = charSequence.charAt(offset);
|
||||
if (ch == ' ' || ch == '\t') {
|
||||
++indentSize;
|
||||
++offset;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -262,17 +264,18 @@ public class VimIndentObject implements VimExtension {
|
||||
@Override
|
||||
public void execute(@NotNull VimEditor editor, @NotNull ExecutionContext context, @NotNull OperatorArguments operatorArguments) {
|
||||
IjVimEditor vimEditor = (IjVimEditor)editor;
|
||||
@NotNull VimStateMachine vimStateMachine = VimStateMachine.Companion.getInstance(vimEditor);
|
||||
int count = Math.max(1, vimStateMachine.getCommandBuilder().getCount());
|
||||
@NotNull KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
@NotNull KeyHandlerState keyHandlerState = KeyHandler.getInstance().getKeyHandlerState();
|
||||
int count = Math.max(1, keyHandlerState.getCommandBuilder().getCount());
|
||||
|
||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!keyHandler.isOperatorPending(editor.getMode(), keyHandlerState)) {
|
||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||
if (vimStateMachine.getMode() instanceof Mode.VISUAL) {
|
||||
if (editor.getMode() instanceof Mode.VISUAL) {
|
||||
EngineVisualGroupKt.vimSetSelection(new IjVimCaret(caret), range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||
} else {
|
||||
InlayHelperKt.moveToInlayAwareOffset(caret, range.getStartOffset());
|
||||
@@ -282,7 +285,7 @@ public class VimIndentObject implements VimExtension {
|
||||
|
||||
});
|
||||
} else {
|
||||
vimStateMachine.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
keyHandlerState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||
textObjectHandler, Command.Type.MOTION,
|
||||
EnumSet.noneOf(CommandFlags.class))));
|
||||
}
|
||||
|
@@ -57,8 +57,9 @@ import com.maddyhome.idea.vim.helper.CharacterHelper.changeCase
|
||||
import com.maddyhome.idea.vim.helper.CharacterHelper.charType
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.NumberType
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.endOffsetInclusive
|
||||
import com.maddyhome.idea.vim.helper.findNumberUnderCursor
|
||||
import com.maddyhome.idea.vim.helper.findNumbersInRange
|
||||
import com.maddyhome.idea.vim.helper.inInsertMode
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
|
||||
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
|
||||
@@ -68,10 +69,11 @@ import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimCaret
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.regexp.VimRegex
|
||||
import com.maddyhome.idea.vim.regexp.match.VimMatchResult
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.vimscript.model.commands.SortOption
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.math.BigInteger
|
||||
@@ -82,7 +84,7 @@ import kotlin.math.max
|
||||
/**
|
||||
* Provides all the insert/replace related functionality
|
||||
*/
|
||||
public class ChangeGroup : VimChangeGroupBase() {
|
||||
class ChangeGroup : VimChangeGroupBase() {
|
||||
private val insertListeners = ContainerUtil.createLockFreeCopyOnWriteList<VimInsertListener>()
|
||||
private val listener: EditorMouseListener = object : EditorMouseListener {
|
||||
override fun mouseClicked(event: EditorMouseEvent) {
|
||||
@@ -93,7 +95,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
public fun editorCreated(editor: Editor?, disposable: Disposable) {
|
||||
fun editorCreated(editor: Editor?, disposable: Disposable) {
|
||||
EventFacade.getInstance().addEditorMouseListener(editor!!, listener, disposable)
|
||||
}
|
||||
|
||||
@@ -101,6 +103,9 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val ijContext = context.ij
|
||||
val doc = vimEditor.editor.document
|
||||
val undo = injector.undo
|
||||
val nanoTime = System.nanoTime()
|
||||
vimEditor.forEachCaret { undo.startInsertSequence(it, it.offset, nanoTime) }
|
||||
CommandProcessor.getInstance().executeCommand(
|
||||
editor.project, {
|
||||
ApplicationManager.getApplication()
|
||||
@@ -196,7 +201,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
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.point, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||
changeCase(editor, caret, caret.offset, (motion as AbsoluteOffset).offset, CharacterHelper.CASE_TOGGLE)
|
||||
motion = injector.motion.getHorizontalMotion(
|
||||
editor,
|
||||
caret,
|
||||
@@ -234,8 +239,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
val lineLength = editor.lineLength(line)
|
||||
if (column < VimMotionGroupBase.LAST_COLUMN && lineLength < column) {
|
||||
val pad =
|
||||
EditorHelper.pad((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context, line, column)
|
||||
val pad = EditorHelper.pad((editor as IjVimEditor).editor, line, column)
|
||||
val offset = editor.getLineEndOffset(line)
|
||||
insertText(editor, caret, offset, pad)
|
||||
}
|
||||
@@ -394,7 +398,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
context: ExecutionContext,
|
||||
range: TextRange,
|
||||
) {
|
||||
val startPos = editor.offsetToBufferPosition(caret.offset.point)
|
||||
val startPos = editor.offsetToBufferPosition(caret.offset)
|
||||
val startOffset = editor.getLineStartForOffset(range.startOffset)
|
||||
val endOffset = editor.getLineEndForOffset(range.endOffset)
|
||||
val ijEditor = (editor as IjVimEditor).editor
|
||||
@@ -450,7 +454,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
dir: Int,
|
||||
operatorArguments: OperatorArguments,
|
||||
) {
|
||||
val start = caret.offset.point
|
||||
val start = caret.offset
|
||||
val end = injector.motion.moveCaretToRelativeLineEnd(editor, caret, lines - 1, true)
|
||||
indentRange(editor, caret, context, TextRange(start, end), 1, dir, operatorArguments)
|
||||
}
|
||||
@@ -484,7 +488,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
|
||||
// Remember the current caret column
|
||||
val intendedColumn = caret.vimLastColumn
|
||||
val indentConfig = create((editor as IjVimEditor).editor, (context as IjEditorExecutionContext).context)
|
||||
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)
|
||||
@@ -567,54 +571,68 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
): Boolean {
|
||||
val startLine = range.startLine
|
||||
val endLine = range.endLine
|
||||
val count = endLine - startLine + 1
|
||||
val count = range.size
|
||||
if (count < 2) {
|
||||
return false
|
||||
}
|
||||
val startOffset = editor.getLineStartOffset(startLine)
|
||||
val endOffset = editor.getLineEndOffset(endLine)
|
||||
return sortTextRange(editor, caret, startOffset, endOffset, lineComparator, sortOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a text range with a comparator. Returns true if a replace was performed, false otherwise.
|
||||
*
|
||||
* @param editor The editor to replace text in
|
||||
* @param start The starting position for the sort
|
||||
* @param end The ending position for the sort
|
||||
* @param lineComparator The comparator to use to sort
|
||||
* @param sortOption The option to sort the range
|
||||
* @return true if able to sort the text, false if not
|
||||
*/
|
||||
private fun sortTextRange(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
start: Int,
|
||||
end: Int,
|
||||
lineComparator: Comparator<String>,
|
||||
sortOption: SortOption,
|
||||
): Boolean {
|
||||
val selectedText = (editor as IjVimEditor).editor.document.getText(TextRangeInterval(start, end))
|
||||
val lines: MutableList<String> = selectedText.split("\n").sortedWith(lineComparator).toMutableList()
|
||||
if (sortOption.unique) {
|
||||
val iterator = lines.iterator()
|
||||
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 || sortOption.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
if (current == previous || sortOptions.ignoreCase && current.equals(previous, ignoreCase = true)) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
previous = current
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lines.size < 1) {
|
||||
if (sortedLines.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
replaceText(editor, caret, start, end, StringUtil.join(lines, "\n"))
|
||||
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
|
||||
*
|
||||
@@ -636,7 +654,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val alpha = nf.contains("alpha")
|
||||
val hex = nf.contains("hex")
|
||||
val octal = nf.contains("octal")
|
||||
val numberRanges = SearchHelper.findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
|
||||
val numberRanges = findNumbersInRange((editor as IjVimEditor).editor, selectedRange, alpha, hex, octal)
|
||||
val newNumbers: MutableList<String?> = ArrayList()
|
||||
for (i in numberRanges.indices) {
|
||||
val numberRange = numberRanges[i]
|
||||
@@ -659,8 +677,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val alpha = nf.contains("alpha")
|
||||
val hex = nf.contains("hex")
|
||||
val octal = nf.contains("octal")
|
||||
val range =
|
||||
SearchHelper.findNumberUnderCursor((editor as IjVimEditor).editor, (caret as IjVimCaret).caret, alpha, hex, octal)
|
||||
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
|
||||
@@ -769,11 +786,11 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
return number
|
||||
}
|
||||
|
||||
public fun addInsertListener(listener: VimInsertListener) {
|
||||
fun addInsertListener(listener: VimInsertListener) {
|
||||
insertListeners.add(listener)
|
||||
}
|
||||
|
||||
public fun removeInsertListener(listener: VimInsertListener) {
|
||||
fun removeInsertListener(listener: VimInsertListener) {
|
||||
insertListeners.remove(listener)
|
||||
}
|
||||
|
||||
|
@@ -8,9 +8,11 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.maddyhome.idea.vim.api.VimCommandGroupBase
|
||||
|
||||
/**
|
||||
* @author Elliot Courant
|
||||
*/
|
||||
@Service
|
||||
internal class CommandGroup : VimCommandGroupBase()
|
||||
|
@@ -11,18 +11,26 @@ package com.maddyhome.idea.vim.group;
|
||||
import com.intellij.execution.impl.ConsoleViewImpl;
|
||||
import com.intellij.find.EditorSearchSession;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.client.ClientAppSession;
|
||||
import com.intellij.openapi.client.ClientKind;
|
||||
import com.intellij.openapi.client.ClientSessionsManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.event.CaretEvent;
|
||||
import com.intellij.openapi.editor.event.CaretListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.helper.*;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt;
|
||||
import com.maddyhome.idea.vim.helper.CommandStateHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.UserDataManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimDocument;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.options.EffectiveOptionValueChangeListener;
|
||||
@@ -32,12 +40,16 @@ import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.intellij.openapi.editor.EditorSettings.*;
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.options;
|
||||
import static com.maddyhome.idea.vim.helper.CaretVisualAttributesHelperKt.updateCaretsVisualAttributes;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.ijOptions;
|
||||
|
||||
/**
|
||||
@@ -49,12 +61,31 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
|
||||
private Boolean isKeyRepeat = null;
|
||||
|
||||
// TODO: Get rid of this custom line converter once we support soft wraps properly
|
||||
// The builtin relative line converter looks like it's using Vim's logical lines for counting, where a Vim logical
|
||||
// line is a buffer line, or a single line representing a fold of several buffer lines. This converter is counting
|
||||
// screen lines (but badly - if you're on the second line of a wrapped line, it still counts like you're on the first.
|
||||
// We really want to use Vim logical lines, but we don't currently support them for movement - we move by screen line.
|
||||
|
||||
private final CaretListener myLineNumbersCaretListener = new CaretListener() {
|
||||
@Override
|
||||
public void caretPositionChanged(@NotNull CaretEvent e) {
|
||||
final boolean requiresRepaint = e.getNewPosition().line != e.getOldPosition().line;
|
||||
if (requiresRepaint && options(injector, new IjVimEditor(e.getEditor())).getRelativenumber()) {
|
||||
repaintRelativeLineNumbers(e.getEditor());
|
||||
// We don't get notified when the IDE's settings change, so make sure we're up-to-date when the caret moves
|
||||
final Editor editor = e.getEditor();
|
||||
boolean relativenumber = ijOptions(injector, new IjVimEditor(editor)).getRelativenumber();
|
||||
if (relativenumber) {
|
||||
if (!hasRelativeLineNumbersInstalled(editor)) {
|
||||
installRelativeLineNumbers(editor);
|
||||
}
|
||||
else {
|
||||
// We must repaint on each caret move, so we update when caret's visual line doesn't match logical line
|
||||
repaintRelativeLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (hasRelativeLineNumbersInstalled(editor)) {
|
||||
removeRelativeLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -67,11 +98,10 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
editor.getCaretModel().addCaretListener(myLineNumbersCaretListener);
|
||||
UserDataManager.setVimEditorGroup(editor, true);
|
||||
|
||||
UserDataManager.setVimLineNumbersInitialState(editor, editor.getSettings().isLineNumbersShown());
|
||||
updateLineNumbers(editor);
|
||||
}
|
||||
|
||||
private void deinitLineNumbers(@NotNull Editor editor, boolean isReleasing) {
|
||||
private void deinitLineNumbers(@NotNull Editor editor) {
|
||||
if (isProjectDisposed(editor) || !supportsVimLineNumbers(editor) || !UserDataManager.getVimEditorGroup(editor)) {
|
||||
return;
|
||||
}
|
||||
@@ -80,14 +110,6 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
UserDataManager.setVimEditorGroup(editor, false);
|
||||
|
||||
removeRelativeLineNumbers(editor);
|
||||
|
||||
// Don't reset the built in line numbers if we're releasing the editor. If we do, EditorSettings.setLineNumbersShown
|
||||
// can cause the editor to refresh settings and can call into FileManagerImpl.getCachedPsiFile AFTER FileManagerImpl
|
||||
// has been disposed (Closing the project with a Find Usages result showing a preview panel is a good repro case).
|
||||
// See IDEA-184351 and VIM-1671
|
||||
if (!isReleasing) {
|
||||
setBuiltinLineNumbers(editor, UserDataManager.getVimLineNumbersInitialState(editor));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
|
||||
@@ -101,41 +123,22 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
|
||||
private static void updateLineNumbers(final @NotNull Editor editor) {
|
||||
final EffectiveOptions options = options(injector, new IjVimEditor(editor));
|
||||
final boolean relativeNumber = options.getRelativenumber();
|
||||
final boolean number = options.getNumber();
|
||||
|
||||
final boolean showBuiltinEditorLineNumbers = shouldShowBuiltinLineNumbers(editor, number, relativeNumber);
|
||||
|
||||
final EditorSettings settings = editor.getSettings();
|
||||
if (settings.isLineNumbersShown() ^ showBuiltinEditorLineNumbers) {
|
||||
// Update line numbers later since it may be called from a caret listener
|
||||
// on the caret move and it may move the caret internally
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (editor.isDisposed()) return;
|
||||
setBuiltinLineNumbers(editor, showBuiltinEditorLineNumbers);
|
||||
});
|
||||
final boolean isLineNumbersShown = editor.getSettings().isLineNumbersShown();
|
||||
if (!isLineNumbersShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (relativeNumber) {
|
||||
final LineNumerationType lineNumerationType = editor.getSettings().getLineNumerationType();
|
||||
if (lineNumerationType == LineNumerationType.RELATIVE || lineNumerationType == LineNumerationType.HYBRID) {
|
||||
if (!hasRelativeLineNumbersInstalled(editor)) {
|
||||
installRelativeLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
else if (hasRelativeLineNumbersInstalled(editor)) {
|
||||
else {
|
||||
removeRelativeLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldShowBuiltinLineNumbers(final @NotNull Editor editor, boolean number, boolean relativeNumber) {
|
||||
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
|
||||
return initialState || number || relativeNumber;
|
||||
}
|
||||
|
||||
private static void setBuiltinLineNumbers(final @NotNull Editor editor, boolean show) {
|
||||
editor.getSettings().setLineNumbersShown(show);
|
||||
}
|
||||
|
||||
private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
|
||||
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
|
||||
}
|
||||
@@ -204,38 +207,46 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
|
||||
public void editorCreated(@NotNull Editor editor) {
|
||||
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
||||
UserDataManager.setVimInitialised(editor, true);
|
||||
|
||||
VimPlugin.getKey().registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
|
||||
initLineNumbers(editor);
|
||||
|
||||
// We add Vim bindings to all opened editors, even read-only editors. We also add bindings to editors that are used
|
||||
// elsewhere in the IDE, rather than just for editing project files. This includes editors used as part of the UI,
|
||||
// such as the VCS commit message, or used as read-only viewers for text output, such as log files in run
|
||||
// configurations or the Git Console tab. And editors are used for interactive stdin/stdout for console-based run
|
||||
// configurations.
|
||||
// Listen for changes to the font size, so we can hide the ex text field/output panel
|
||||
if (editor instanceof EditorEx editorEx) {
|
||||
editorEx.addPropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
||||
}
|
||||
|
||||
// We add Vim bindings to all opened editors, including editors used as UI controls rather than just project file
|
||||
// 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 for interactive editors. Recognising these can be a bit tricky.
|
||||
// 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, the interactive stdin/stdout console output is hosted in a read-only editor, but it can still be
|
||||
// edited. The `ConsoleViewImpl` class installs a typing handler that ignores the editor's `isViewer` property and
|
||||
// allows typing if the associated process (if any) is still running. We can get the editor's console view and check
|
||||
// this ourselves, but we have to wait until the editor has finished initialising before it's available in user
|
||||
// data.
|
||||
// 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.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
||||
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)) {
|
||||
!CommandStateHelper.inInsertMode(editor) &&
|
||||
editor.getEditorKind() != EditorKind.DIFF) {
|
||||
switchToInsertMode.run();
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
@@ -246,15 +257,17 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
switchToInsertMode.run();
|
||||
}
|
||||
});
|
||||
updateCaretsVisualAttributes(editor);
|
||||
updateCaretsVisualAttributes(new IjVimEditor(editor));
|
||||
}
|
||||
|
||||
public void editorDeinit(@NotNull Editor editor, boolean isReleased) {
|
||||
deinitLineNumbers(editor, isReleased);
|
||||
public void editorDeinit(@NotNull Editor editor) {
|
||||
deinitLineNumbers(editor);
|
||||
UserDataManager.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
||||
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
||||
if (editor instanceof EditorEx editorEx) {
|
||||
editorEx.removePropertyChangeListener(FontSizeChangeListener.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyIdeaJoin(@Nullable Project project, @NotNull VimEditor editor) {
|
||||
@@ -284,6 +297,18 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
notifyIdeaJoin(((IjVimEditor) editor).getEditor().getProject(), editor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCaretsVisualAttributes(@NotNull VimEditor editor) {
|
||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCaretsVisualPosition(@NotNull VimEditor editor) {
|
||||
Editor ijEditor = ((IjVimEditor) editor).getEditor();
|
||||
CaretVisualAttributesHelperKt.updateCaretsVisualAttributes(ijEditor);
|
||||
}
|
||||
|
||||
public static class NumberChangeListener implements EffectiveOptionValueChangeListener {
|
||||
public static NumberChangeListener INSTANCE = new NumberChangeListener();
|
||||
|
||||
@@ -304,17 +329,18 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
private static class RelativeLineNumberConverter implements LineNumberConverter {
|
||||
@Override
|
||||
public Integer convert(@NotNull Editor editor, int lineNumber) {
|
||||
final boolean number = options(injector, new IjVimEditor(editor)).getNumber();
|
||||
final IjVimEditor ijVimEditor = new IjVimEditor(editor);
|
||||
final boolean number = options(injector, ijVimEditor).getNumber();
|
||||
final int caretLine = editor.getCaretModel().getLogicalPosition().line;
|
||||
|
||||
// lineNumber is 1 based
|
||||
if (number && (lineNumber - 1) == caretLine) {
|
||||
return lineNumber;
|
||||
if ((lineNumber - 1) == caretLine) {
|
||||
return number ? lineNumber : 0;
|
||||
}
|
||||
else {
|
||||
final int visualLine = new IjVimEditor(editor).bufferLineToVisualLine(lineNumber - 1);
|
||||
final int currentVisualLine = new IjVimEditor(editor).bufferLineToVisualLine(caretLine);
|
||||
return Math.abs(currentVisualLine - visualLine);
|
||||
final int visualLine = ijVimEditor.bufferLineToVisualLine(lineNumber - 1);
|
||||
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
|
||||
return Math.abs(caretVisualLine - visualLine);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,20 +350,80 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> localEditors() {
|
||||
return HelperKt.localEditors().stream()
|
||||
public @NotNull Collection<VimEditor> getEditorsRaw() {
|
||||
return getLocalEditors()
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> localEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return HelperKt.localEditors(document).stream()
|
||||
public Collection<VimEditor> getEditors() {
|
||||
return getLocalEditors()
|
||||
.filter(UserDataManager::getVimInitialised)
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<VimEditor> getEditors(@NotNull VimDocument buffer) {
|
||||
final Document document = ((IjVimDocument)buffer).getDocument();
|
||||
return getLocalEditors()
|
||||
.filter(editor -> UserDataManager.getVimInitialised(editor) && editor.getDocument().equals(document))
|
||||
.map(IjVimEditor::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Stream<Editor> getLocalEditors() {
|
||||
// Always fetch local editors. If we're hosting a Code With Me session, any connected guests will create hidden
|
||||
// editors to handle syntax highlighting, completion requests, etc. We need to make sure that IdeaVim only makes
|
||||
// changes (e.g., adding search highlights) to local editors so things don't incorrectly flow through to any Clients.
|
||||
// In non-CWM scenarios, or if IdeaVim is installed on the Client, there are only ever local editors, so this will
|
||||
// also work there. In Gateway remote development scenarios, IdeaVim should not be installed on the host, only the
|
||||
// Client, so all should work there too.
|
||||
// Note that most IdeaVim operations are in response to interactive keystrokes, which would mean that
|
||||
// ClientEditorManager.getCurrentInstance would return local editors. However, some operations are in response to
|
||||
// events such as document change (to update search highlights), and these can come from CWM guests, and we'd get
|
||||
// the remote editors.
|
||||
// This invocation will always get local editors, regardless of the current context.
|
||||
List<ClientAppSession> appSessions = ClientSessionsManager.getAppSessions(ClientKind.ALL);
|
||||
if (!appSessions.isEmpty()) {
|
||||
ClientAppSession localSession = appSessions.get(0);
|
||||
return localSession.getService(ClientEditorManager.class).editors();
|
||||
}
|
||||
else {
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to property changes from the editor to hide ex text field/output panel when the editor's font is zoomed
|
||||
*/
|
||||
private static class FontSizeChangeListener implements PropertyChangeListener {
|
||||
public static FontSizeChangeListener INSTANCE = new FontSizeChangeListener();
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (VimPlugin.isNotEnabled()) return;
|
||||
if (evt.getPropertyName().equals(EditorEx.PROP_FONT_SIZE)) {
|
||||
Object source = evt.getSource();
|
||||
if (source instanceof Editor editor) {
|
||||
// The editor is being zoomed, so hide the command line or output panel, if they're being shown. On the one
|
||||
// hand, it's a little rude to cancel a command line for the user, but on the other, the panels obscure the
|
||||
// zoom indicator, so it looks nicer if we hide them.
|
||||
// Note that IDE scale is handled by LafManager.lookAndFeelChanged
|
||||
VimCommandLine activeCommandLine = injector.getCommandLine().getActiveCommandLine();
|
||||
if (activeCommandLine != null) {
|
||||
injector.getProcessGroup().cancelExEntry(new IjVimEditor(editor), false);
|
||||
}
|
||||
ExOutputModel exOutputModel = ExOutputModel.tryGetInstance(editor);
|
||||
if (exOutputModel != null) {
|
||||
exOutputModel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.editor.Editor
|
||||
|
||||
@Service
|
||||
internal class EditorHolderService {
|
||||
var editor: Editor? = null
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(): EditorHolderService = service()
|
||||
}
|
||||
}
|
@@ -1,451 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelperRt;
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper;
|
||||
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
|
||||
|
||||
public class FileGroup extends VimFileBase {
|
||||
public boolean openFile(@NotNull String filename, @NotNull ExecutionContext context) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("openFile(" + filename + ")");
|
||||
}
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext()); // API change - don't merge
|
||||
if (project == null) return false;
|
||||
|
||||
VirtualFile found = findFile(filename, project);
|
||||
|
||||
if (found != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("found file: " + found);
|
||||
}
|
||||
// Can't open a file unless it has a known file type. The next call will return the known type.
|
||||
// If unknown, IDEA will prompt the user to pick a type.
|
||||
FileType type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project);
|
||||
|
||||
//noinspection IfStatementWithIdenticalBranches
|
||||
if (type != null) {
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project);
|
||||
fem.openFile(found, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// There was no type and user didn't pick one. Don't open the file
|
||||
// Return true here because we found the file but the user canceled by not picking a type.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable VirtualFile findFile(@NotNull String filename, @NotNull Project project) {
|
||||
VirtualFile found;
|
||||
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
||||
// it only supports forward slash on Unix (tested on Mac)
|
||||
// VFS works with both directory separators (tested on Mac and Windows)
|
||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||
String relativePath = filename.substring(2);
|
||||
String dir = System.getProperty("user.home");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("home dir file");
|
||||
logger.debug("looking for " + relativePath + " in " + dir);
|
||||
}
|
||||
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, relativePath));
|
||||
}
|
||||
else {
|
||||
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
|
||||
|
||||
if (found == null) {
|
||||
found = findByNameInContentRoots(filename, project);
|
||||
if (found == null) {
|
||||
found = findByNameInProject(filename, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private VirtualFile findByNameInContentRoots(@NotNull String filename, @NotNull Project project) {
|
||||
VirtualFile found = null;
|
||||
ProjectRootManager prm = ProjectRootManager.getInstance(project);
|
||||
VirtualFile[] roots = prm.getContentRoots();
|
||||
for (int i = 0; i < roots.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("root[" + i + "] = " + roots[i].getPath());
|
||||
}
|
||||
found = roots[i].findFileByRelativePath(filename);
|
||||
if (found != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile findByNameInProject(@NotNull String filename, @NotNull Project project) {
|
||||
GlobalSearchScope projectScope = ProjectScope.getProjectScope(project);
|
||||
Collection<VirtualFile> names = FilenameIndex.getVirtualFilesByName(filename, projectScope);
|
||||
if (!names.isEmpty()) {
|
||||
return names.stream().findFirst().get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current editor.
|
||||
*/
|
||||
@Override
|
||||
public void closeFile(@NotNull VimEditor editor, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||
if (project != null) {
|
||||
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||
final VirtualFile virtualFile = fileEditorManager.getCurrentFile();
|
||||
|
||||
if (virtualFile != null && window != null) {
|
||||
// During the work on VIM-2912 I've changed the close function to this one.
|
||||
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
||||
//window.getManager().closeFile(virtualFile, true, false);
|
||||
window.closeFile(virtualFile);
|
||||
|
||||
// Get focus after closing tab
|
||||
window.requestFocus(true);
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes editor.
|
||||
*/
|
||||
@Override
|
||||
public void closeFile(int number, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return;
|
||||
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
|
||||
final EditorWindow window = fileEditorManager.getCurrentWindow();
|
||||
VirtualFile[] editors = fileEditorManager.getOpenFiles();
|
||||
if (window != null) {
|
||||
if (number >= 0 && number < editors.length) {
|
||||
fileEditorManager.closeFile(editors[number], window);
|
||||
}
|
||||
} if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves specific file in the project.
|
||||
*/
|
||||
@Override
|
||||
public void saveFile(@NotNull ExecutionContext context) {
|
||||
NativeAction action;
|
||||
if (globalIjOptions(injector).getIdeawrite().contains(IjOptionConstants.ideawrite_all)) {
|
||||
action = injector.getNativeActionManager().getSaveAll();
|
||||
}
|
||||
else {
|
||||
action = injector.getNativeActionManager().getSaveCurrent();
|
||||
}
|
||||
ExecuteExtensionKt.execute(action, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all files in the project.
|
||||
*/
|
||||
public void saveFiles(@NotNull ExecutionContext context) {
|
||||
ExecuteExtensionKt.execute(VimInjectorKt.getInjector().getNativeActionManager().getSaveAll(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
@Override
|
||||
public boolean selectFile(int count, @NotNull ExecutionContext context) {
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return false;
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||
VirtualFile[] editors = fem.getOpenFiles();
|
||||
if (count == 99) {
|
||||
count = editors.length - 1;
|
||||
}
|
||||
if (count < 0 || count >= editors.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fem.openFile(editors[count], true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
public void selectNextFile(int count, @NotNull ExecutionContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(((IjEditorExecutionContext) context).getContext());
|
||||
if (project == null) return;
|
||||
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||
VirtualFile[] editors = fem.getOpenFiles();
|
||||
VirtualFile current = fem.getSelectedFiles()[0];
|
||||
for (int i = 0; i < editors.length; i++) {
|
||||
if (editors[i].equals(current)) {
|
||||
int pos = (i + (count % editors.length) + editors.length) % editors.length;
|
||||
|
||||
fem.openFile(editors[pos], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects previous editor tab.
|
||||
*/
|
||||
@Override
|
||||
public void selectPreviousTab(@NotNull ExecutionContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(((DataContext)context.getContext()));
|
||||
if (project == null) return;
|
||||
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||
if (vf != null && vf.isValid()) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||
}
|
||||
else {
|
||||
VimPlugin.indicateError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous tab.
|
||||
*/
|
||||
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||
if (project == null) return null;
|
||||
VirtualFile vf = LastTabService.getInstance(project).getLastTab();
|
||||
if (vf != null && vf.isValid()) {
|
||||
return vf;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable Editor selectEditor(Project project, @NotNull VirtualFile file) {
|
||||
FileEditorManager fMgr = FileEditorManager.getInstance(project);
|
||||
FileEditor[] feditors = fMgr.openFile(file, true);
|
||||
if (feditors.length > 0) {
|
||||
if (feditors[0] instanceof TextEditor) {
|
||||
Editor editor = ((TextEditor)feditors[0]).getEditor();
|
||||
if (!editor.isDisposed()) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayLocationInfo(@NotNull VimEditor vimEditor) {
|
||||
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
StringBuilder msg = new StringBuilder();
|
||||
Document doc = editor.getDocument();
|
||||
|
||||
if (!(VimStateMachine.Companion.getInstance(new IjVimEditor(editor)).getMode() instanceof Mode.VISUAL)) {
|
||||
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lp.line);
|
||||
int endoff = doc.getLineEndOffset(lp.line);
|
||||
if (endoff < EditorHelperRt.getFileSize(editor) && doc.getCharsSequence().charAt(endoff) == '\n') {
|
||||
endoff--;
|
||||
}
|
||||
int ecol = endoff - doc.getLineStartOffset(lp.line);
|
||||
LogicalPosition elp = editor.offsetToLogicalPosition(endoff);
|
||||
|
||||
msg.append("Col ").append(col + 1);
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1);
|
||||
}
|
||||
|
||||
msg.append(" of ").append(ecol + 1);
|
||||
if (ecol != elp.column) {
|
||||
msg.append("-").append(elp.column + 1);
|
||||
}
|
||||
|
||||
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
|
||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total);
|
||||
|
||||
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||
|
||||
msg.append("; Word ").append(cp.getPosition()).append(" of ").append(cp.getCount());
|
||||
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
int size = EditorHelperRt.getFileSize(editor);
|
||||
|
||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size);
|
||||
}
|
||||
else {
|
||||
msg.append("Selected ");
|
||||
|
||||
TextRange vr = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
|
||||
editor.getSelectionModel().getBlockSelectionEnds());
|
||||
vr.normalize();
|
||||
|
||||
int lines;
|
||||
SearchHelper.CountPosition cp = SearchHelper.countWords(editor);
|
||||
int words = cp.getCount();
|
||||
int word = 0;
|
||||
if (vr.isMultiple()) {
|
||||
lines = vr.size();
|
||||
int cols = vr.getMaxLength();
|
||||
|
||||
msg.append(cols).append(" Cols; ");
|
||||
|
||||
for (int i = 0; i < vr.size(); i++) {
|
||||
cp = SearchHelper.countWords(editor, vr.getStartOffsets()[i], vr.getEndOffsets()[i] - 1);
|
||||
word += cp.getCount();
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogicalPosition slp = editor.offsetToLogicalPosition(vr.getStartOffset());
|
||||
LogicalPosition elp = editor.offsetToLogicalPosition(vr.getEndOffset());
|
||||
|
||||
lines = elp.line - slp.line + 1;
|
||||
|
||||
cp = SearchHelper.countWords(editor, vr.getStartOffset(), vr.getEndOffset() - 1);
|
||||
word = cp.getCount();
|
||||
}
|
||||
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
|
||||
msg.append(lines).append(" of ").append(total).append(" Lines");
|
||||
|
||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words");
|
||||
|
||||
int chars = vr.getSelectionCount();
|
||||
int size = EditorHelperRt.getFileSize(editor);
|
||||
|
||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters");
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayFileInfo(@NotNull VimEditor vimEditor, boolean fullPath) {
|
||||
Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
StringBuilder msg = new StringBuilder();
|
||||
VirtualFile vf = EditorHelper.getVirtualFile(editor);
|
||||
if (vf != null) {
|
||||
msg.append('"');
|
||||
if (fullPath) {
|
||||
msg.append(vf.getPath());
|
||||
}
|
||||
else {
|
||||
Project project = editor.getProject();
|
||||
if (project != null) {
|
||||
VirtualFile root = ProjectRootManager.getInstance(project).getFileIndex().getContentRootForFile(vf);
|
||||
if (root != null) {
|
||||
msg.append(vf.getPath().substring(root.getPath().length() + 1));
|
||||
}
|
||||
else {
|
||||
msg.append(vf.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.append("\" ");
|
||||
}
|
||||
else {
|
||||
msg.append("\"[No File]\" ");
|
||||
}
|
||||
|
||||
Document doc = editor.getDocument();
|
||||
if (!doc.isWritable()) {
|
||||
msg.append("[RO] ");
|
||||
}
|
||||
else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
||||
msg.append("[+] ");
|
||||
}
|
||||
|
||||
int lline = editor.getCaretModel().getLogicalPosition().line;
|
||||
int total = new IjVimEditor(editor).lineCount();
|
||||
int pct = (int)((float)lline / (float)total * 100f + 0.5);
|
||||
|
||||
msg.append("line ").append(lline + 1).append(" of ").append(total);
|
||||
msg.append(" --").append(pct).append("%-- ");
|
||||
|
||||
LogicalPosition lp = editor.getCaretModel().getLogicalPosition();
|
||||
int col = editor.getCaretModel().getOffset() - doc.getLineStartOffset(lline);
|
||||
|
||||
msg.append("col ").append(col + 1);
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1);
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString());
|
||||
}
|
||||
|
||||
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||
|
||||
/**
|
||||
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
|
||||
*/
|
||||
public static void fileEditorManagerSelectionChangedCallback(@NotNull FileEditorManagerEvent event) {
|
||||
// The user has changed the editor they are working with - exit insert/replace mode, and complete any
|
||||
// appropriate repeat
|
||||
if (event.getOldFile() != null) {
|
||||
LastTabService.getInstance(event.getManager().getProject()).setLastTab(event.getOldFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
444
src/main/java/com/maddyhome/idea/vim/group/FileGroup.kt
Normal file
444
src/main/java/com/maddyhome/idea/vim/group/FileGroup.kt
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.fileEditor.impl.EditorsSplitters
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimFileBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.group.LastTabService.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper.message
|
||||
import com.maddyhome.idea.vim.helper.countWords
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor
|
||||
import com.maddyhome.idea.vim.newapi.execute
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class FileGroup : VimFileBase() {
|
||||
override fun openFile(filename: String, context: ExecutionContext): Boolean {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("openFile($filename)")
|
||||
}
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context)
|
||||
?: return false // API change - don't merge
|
||||
|
||||
val found = findFile(filename, project)
|
||||
|
||||
if (found != null) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("found file: $found")
|
||||
}
|
||||
// Can't open a file unless it has a known file type. The next call will return the known type.
|
||||
// If unknown, IDEA will prompt the user to pick a type.
|
||||
val type = FileTypeManager.getInstance().getKnownFileTypeOrAssociate(found, project)
|
||||
|
||||
if (type != null) {
|
||||
val fem = FileEditorManager.getInstance(project)
|
||||
fem.openFile(found, true)
|
||||
|
||||
return true
|
||||
} else {
|
||||
// There was no type and user didn't pick one. Don't open the file
|
||||
// Return true here because we found the file but the user canceled by not picking a type.
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
VimPlugin.showMessage(message("unable.to.find.0", filename))
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun findFile(filename: String, project: Project): VirtualFile? {
|
||||
var found: VirtualFile?
|
||||
// Vim supports both ~/ and ~\ (tested on Mac and Windows). On Windows, it supports forward- and back-slashes, but
|
||||
// it only supports forward slash on Unix (tested on Mac)
|
||||
// VFS works with both directory separators (tested on Mac and Windows)
|
||||
if (filename.startsWith("~/") || filename.startsWith("~\\")) {
|
||||
val relativePath = filename.substring(2)
|
||||
val dir = System.getProperty("user.home")
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("home dir file")
|
||||
logger.debug("looking for $relativePath in $dir")
|
||||
}
|
||||
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(dir, relativePath))
|
||||
} else {
|
||||
found = LocalFileSystem.getInstance().findFileByIoFile(File(filename))
|
||||
|
||||
if (found == null) {
|
||||
found = findByNameInContentRoots(filename, project)
|
||||
if (found == null) {
|
||||
found = findByNameInProject(filename, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
private fun findByNameInContentRoots(filename: String, project: Project): VirtualFile? {
|
||||
var found: VirtualFile? = null
|
||||
val prm = ProjectRootManager.getInstance(project)
|
||||
val roots = prm.contentRoots
|
||||
for (i in roots.indices) {
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("root[" + i + "] = " + roots[i].path)
|
||||
}
|
||||
found = roots[i].findFileByRelativePath(filename)
|
||||
if (found != null) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current editor.
|
||||
*/
|
||||
override fun closeFile(editor: VimEditor, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext))
|
||||
if (project != null) {
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
val window = fileEditorManager.currentWindow
|
||||
val virtualFile = fileEditorManager.currentFile
|
||||
|
||||
if (virtualFile != null && window != null) {
|
||||
// During the work on VIM-2912 I've changed the close function to this one.
|
||||
// However, the function with manager seems to work weirdly and it causes VIM-2953
|
||||
//window.getManager().closeFile(virtualFile, true, false);
|
||||
window.closeFile(virtualFile)
|
||||
|
||||
// Get focus after closing tab
|
||||
window.requestFocus(true)
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes editor.
|
||||
*/
|
||||
override fun closeFile(number: Int, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
||||
val fileEditorManager = FileEditorManagerEx.getInstanceEx(project)
|
||||
val window = fileEditorManager.currentWindow
|
||||
val editors = fileEditorManager.openFiles
|
||||
if (window != null) {
|
||||
if (number >= 0 && number < editors.size) {
|
||||
fileEditorManager.closeFile(editors[number], window)
|
||||
}
|
||||
}
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
// This thing doesn't have an implementation in test mode
|
||||
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves specific file in the project.
|
||||
*/
|
||||
override fun saveFile(context: ExecutionContext) {
|
||||
val action = if (injector.globalIjOptions().ideawrite.contains(IjOptionConstants.ideawrite_all)) {
|
||||
injector.nativeActionManager.saveAll
|
||||
} else {
|
||||
injector.nativeActionManager.saveCurrent
|
||||
}
|
||||
action.execute(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all files in the project.
|
||||
*/
|
||||
override fun saveFiles(context: ExecutionContext) {
|
||||
injector.nativeActionManager.saveAll.execute(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
override fun selectFile(count: Int, context: ExecutionContext): Boolean {
|
||||
var count = count
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return false
|
||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
||||
val editors = fem.openFiles
|
||||
if (count == 99) {
|
||||
count = editors.size - 1
|
||||
}
|
||||
if (count < 0 || count >= editors.size) {
|
||||
return false
|
||||
}
|
||||
|
||||
fem.openFile(editors[count], true)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects then next or previous editor.
|
||||
*/
|
||||
override fun selectNextFile(count: Int, context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context as IjEditorExecutionContext).context) ?: return
|
||||
val fem = FileEditorManager.getInstance(project) // API change - don't merge
|
||||
val editors = fem.openFiles
|
||||
val current = fem.selectedFiles[0]
|
||||
for (i in editors.indices) {
|
||||
if (editors[i] == current) {
|
||||
val pos = (i + (count % editors.size) + editors.size) % editors.size
|
||||
|
||||
fem.openFile(editors[pos], true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects previous editor tab.
|
||||
*/
|
||||
override fun selectPreviousTab(context: ExecutionContext) {
|
||||
val project = PlatformDataKeys.PROJECT.getData((context.context as DataContext)) ?: return
|
||||
val vf = getInstance(project).lastTab
|
||||
if (vf != null && vf.isValid) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true)
|
||||
} else {
|
||||
VimPlugin.indicateError()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous tab.
|
||||
*/
|
||||
fun getPreviousTab(context: DataContext): VirtualFile? {
|
||||
val project = PlatformDataKeys.PROJECT.getData(context) ?: return null
|
||||
val vf = getInstance(project).lastTab
|
||||
if (vf != null && vf.isValid) {
|
||||
return vf
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun selectEditor(project: Project, file: VirtualFile): Editor? {
|
||||
val fMgr = FileEditorManager.getInstance(project)
|
||||
val feditors = fMgr.openFile(file, true)
|
||||
if (feditors.size > 0) {
|
||||
if (feditors[0] is TextEditor) {
|
||||
val editor = (feditors[0] as TextEditor).editor
|
||||
if (!editor.isDisposed) {
|
||||
return editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun displayLocationInfo(vimEditor: VimEditor) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
val doc = editor.document
|
||||
|
||||
if (getInstance(IjVimEditor(editor)).mode !is VISUAL) {
|
||||
val lp = editor.caretModel.logicalPosition
|
||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lp.line)
|
||||
var endoff = doc.getLineEndOffset(lp.line)
|
||||
if (endoff < editor.fileSize && doc.charsSequence[endoff] == '\n') {
|
||||
endoff--
|
||||
}
|
||||
val ecol = endoff - doc.getLineStartOffset(lp.line)
|
||||
val elp = editor.offsetToLogicalPosition(endoff)
|
||||
|
||||
msg.append("Col ").append(col + 1)
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1)
|
||||
}
|
||||
|
||||
msg.append(" of ").append(ecol + 1)
|
||||
if (ecol != elp.column) {
|
||||
msg.append("-").append(elp.column + 1)
|
||||
}
|
||||
|
||||
val lline = editor.caretModel.logicalPosition.line
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append("; Line ").append(lline + 1).append(" of ").append(total)
|
||||
|
||||
val cp = countWords(vimEditor)
|
||||
|
||||
msg.append("; Word ").append(cp.position).append(" of ").append(cp.count)
|
||||
|
||||
val offset = editor.caretModel.offset
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; Character ").append(offset + 1).append(" of ").append(size)
|
||||
} else {
|
||||
msg.append("Selected ")
|
||||
|
||||
val vr = TextRange(
|
||||
editor.selectionModel.blockSelectionStarts,
|
||||
editor.selectionModel.blockSelectionEnds
|
||||
)
|
||||
vr.normalize()
|
||||
|
||||
val lines: Int
|
||||
var cp = countWords(vimEditor)
|
||||
val words = cp.count
|
||||
var word = 0
|
||||
if (vr.isMultiple) {
|
||||
lines = vr.size()
|
||||
val cols = vr.maxLength
|
||||
|
||||
msg.append(cols).append(" Cols; ")
|
||||
|
||||
for (i in 0 until vr.size()) {
|
||||
cp = countWords(vimEditor, vr.startOffsets[i], (vr.endOffsets[i] - 1).toLong())
|
||||
word += cp.count
|
||||
}
|
||||
} else {
|
||||
val slp = editor.offsetToLogicalPosition(vr.startOffset)
|
||||
val elp = editor.offsetToLogicalPosition(vr.endOffset)
|
||||
|
||||
lines = elp.line - slp.line + 1
|
||||
|
||||
cp = countWords(vimEditor, vr.startOffset, (vr.endOffset - 1).toLong())
|
||||
word = cp.count
|
||||
}
|
||||
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
|
||||
msg.append(lines).append(" of ").append(total).append(" Lines")
|
||||
|
||||
msg.append("; ").append(word).append(" of ").append(words).append(" Words")
|
||||
|
||||
val chars = vr.selectionCount
|
||||
val size = editor.fileSize
|
||||
|
||||
msg.append("; ").append(chars).append(" of ").append(size).append(" Characters")
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString())
|
||||
}
|
||||
|
||||
override fun displayFileInfo(vimEditor: VimEditor, fullPath: Boolean) {
|
||||
val editor = (vimEditor as IjVimEditor).editor
|
||||
val msg = StringBuilder()
|
||||
val vf = EditorHelper.getVirtualFile(editor)
|
||||
if (vf != null) {
|
||||
msg.append('"')
|
||||
if (fullPath) {
|
||||
msg.append(vf.path)
|
||||
} else {
|
||||
val project = editor.project
|
||||
if (project != null) {
|
||||
val root = ProjectRootManager.getInstance(project).fileIndex.getContentRootForFile(vf)
|
||||
if (root != null) {
|
||||
msg.append(vf.path.substring(root.path.length + 1))
|
||||
} else {
|
||||
msg.append(vf.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.append("\" ")
|
||||
} else {
|
||||
msg.append("\"[No File]\" ")
|
||||
}
|
||||
|
||||
val doc = editor.document
|
||||
if (!doc.isWritable) {
|
||||
msg.append("[RO] ")
|
||||
} else if (FileDocumentManager.getInstance().isDocumentUnsaved(doc)) {
|
||||
msg.append("[+] ")
|
||||
}
|
||||
|
||||
val lline = editor.caretModel.logicalPosition.line
|
||||
val total = IjVimEditor(editor).lineCount()
|
||||
val pct = (lline.toFloat() / total.toFloat() * 100f + 0.5).toInt()
|
||||
|
||||
msg.append("line ").append(lline + 1).append(" of ").append(total)
|
||||
msg.append(" --").append(pct).append("%-- ")
|
||||
|
||||
val lp = editor.caretModel.logicalPosition
|
||||
val col = editor.caretModel.offset - doc.getLineStartOffset(lline)
|
||||
|
||||
msg.append("col ").append(col + 1)
|
||||
if (col != lp.column) {
|
||||
msg.append("-").append(lp.column + 1)
|
||||
}
|
||||
|
||||
VimPlugin.showMessage(msg.toString())
|
||||
}
|
||||
|
||||
override fun selectEditor(projectId: String, documentPath: String, protocol: String?): VimEditor? {
|
||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol) ?: return null
|
||||
val virtualFile = fileSystem.findFileByPath(documentPath) ?: return null
|
||||
|
||||
val project = Arrays.stream(ProjectManager.getInstance().openProjects)
|
||||
.filter { p: Project? -> injector.file.getProjectId(p!!) == projectId }
|
||||
.findFirst().orElseThrow()
|
||||
|
||||
val editor = selectEditor(project, virtualFile) ?: return null
|
||||
return IjVimEditor(editor)
|
||||
}
|
||||
|
||||
override fun getProjectId(project: Any): String {
|
||||
require(project is Project)
|
||||
return project.name + "-" + project.locationHash
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun findByNameInProject(filename: String, project: Project): VirtualFile? {
|
||||
val projectScope = ProjectScope.getProjectScope(project)
|
||||
val names = FilenameIndex.getVirtualFilesByName(filename, projectScope)
|
||||
if (!names.isEmpty()) {
|
||||
return names.stream().findFirst().get()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val logger = Logger.getInstance(
|
||||
FileGroup::class.java.name
|
||||
)
|
||||
|
||||
/**
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
*/
|
||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||
if (event.oldFile != null) {
|
||||
getInstance(event.manager.project).lastTab = event.oldFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,25 +19,22 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
* options
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
public var ide: String by optionProperty(IjOptions.ide)
|
||||
public var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||
public var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||
public val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
||||
public var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
||||
public val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||
public var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||
public var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||
public var showmodewidget: Boolean by optionProperty(IjOptions.showmodewidget)
|
||||
open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesBase(scope) {
|
||||
var ide: String by optionProperty(IjOptions.ide)
|
||||
var ideamarks: Boolean by optionProperty(IjOptions.ideamarks)
|
||||
var ideastatusicon: String by optionProperty(IjOptions.ideastatusicon)
|
||||
val ideavimsupport: StringListOptionValue by optionProperty(IjOptions.ideavimsupport)
|
||||
var ideawrite: String by optionProperty(IjOptions.ideawrite)
|
||||
val lookupkeys: StringListOptionValue by optionProperty(IjOptions.lookupkeys)
|
||||
var trackactionids: Boolean by optionProperty(IjOptions.trackactionids)
|
||||
var visualdelay: Int by optionProperty(IjOptions.visualdelay)
|
||||
|
||||
// Temporary options to control work-in-progress behaviour
|
||||
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||
var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,8 +42,19 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
|
||||
*
|
||||
* As a convenience, this class also provides access to the IntelliJ specific global options, via inheritance.
|
||||
*/
|
||||
public class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
|
||||
public var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
||||
public var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
||||
public var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
||||
class EffectiveIjOptions(scope: OptionAccessScope.EFFECTIVE): GlobalIjOptions(scope) {
|
||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
||||
var breakindent: Boolean by optionProperty(IjOptions.breakindent)
|
||||
val colorcolumn: StringListOptionValue by optionProperty(IjOptions.colorcolumn)
|
||||
var cursorline: Boolean by optionProperty(IjOptions.cursorline)
|
||||
var fileformat: String by optionProperty(IjOptions.fileformat)
|
||||
var list: Boolean by optionProperty(IjOptions.list)
|
||||
var relativenumber: Boolean by optionProperty(IjOptions.relativenumber)
|
||||
var textwidth: Int by optionProperty(IjOptions.textwidth)
|
||||
var wrap: Boolean by optionProperty(IjOptions.wrap)
|
||||
|
||||
// IntelliJ specific options
|
||||
var ideacopypreprocess: Boolean by optionProperty(IjOptions.ideacopypreprocess)
|
||||
var ideajoin: Boolean by optionProperty(IjOptions.ideajoin)
|
||||
var idearefactormode: String by optionProperty(IjOptions.idearefactormode)
|
||||
}
|
||||
|
@@ -10,9 +10,14 @@ package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.maddyhome.idea.vim.api.Options
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.ex.exExceptionMessage
|
||||
import com.maddyhome.idea.vim.options.NumberOption
|
||||
import com.maddyhome.idea.vim.options.Option
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.GLOBAL_OR_LOCAL_TO_BUFFER
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_BUFFER
|
||||
import com.maddyhome.idea.vim.options.OptionDeclaredScope.LOCAL_TO_WINDOW
|
||||
import com.maddyhome.idea.vim.options.StringListOption
|
||||
import com.maddyhome.idea.vim.options.StringOption
|
||||
import com.maddyhome.idea.vim.options.ToggleOption
|
||||
@@ -21,9 +26,9 @@ import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
public object IjOptions {
|
||||
object IjOptions {
|
||||
|
||||
public fun initialise() {
|
||||
fun initialise() {
|
||||
// Calling this method allows for deterministic initialisation of IjOptions, specifically initialising the
|
||||
// properties and registering the IJ specific options. Once added, they can be safely accessed by name, e.g. by the
|
||||
// implementation of `:set` while executing ~/.ideavimrc
|
||||
@@ -33,17 +38,64 @@ public object IjOptions {
|
||||
Options.overrideDefaultValue(Options.clipboard, VimString("ideaput,autoselect,exclude:cons\\|linux"))
|
||||
}
|
||||
|
||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true))
|
||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true))
|
||||
public val ide: StringOption = addOption(
|
||||
// Vim options that are implemented purely by existing IntelliJ features and not used by vim-engine
|
||||
val breakindent: ToggleOption = addOption(ToggleOption("breakindent", LOCAL_TO_WINDOW, "bri", false))
|
||||
val colorcolumn: StringListOption = addOption(object : StringListOption("colorcolumn", LOCAL_TO_WINDOW, "cc", "") {
|
||||
override fun checkIfValueValid(value: VimDataType, token: String) {
|
||||
super.checkIfValueValid(value, token)
|
||||
if (value != VimString.EMPTY) {
|
||||
// Each element in the comma-separated string list needs to be a number. No spaces. Vim supports numbers
|
||||
// beginning "+" or "-" to draw a highlight column relative to the 'textwidth' value. We don't fully support
|
||||
// that, but we do automatically add "+0" because IntelliJ always displays the right margin
|
||||
split((value as VimString).asString()).forEach {
|
||||
if (!it.matches(Regex("[+-]?[0-9]+"))) {
|
||||
throw exExceptionMessage("E474", token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
val cursorline: ToggleOption = addOption(ToggleOption("cursorline", LOCAL_TO_WINDOW, "cul", false))
|
||||
val list: ToggleOption = addOption(ToggleOption("list", LOCAL_TO_WINDOW, "list", false))
|
||||
val relativenumber: ToggleOption = addOption(ToggleOption("relativenumber", LOCAL_TO_WINDOW, "rnu", false))
|
||||
val textwidth: NumberOption = addOption(UnsignedNumberOption("textwidth", LOCAL_TO_BUFFER, "tw", 0))
|
||||
val wrap: ToggleOption = addOption(ToggleOption("wrap", LOCAL_TO_WINDOW, "wrap", true))
|
||||
|
||||
// These options are not explicitly listed as local-noglobal in Vim's help, but are set when a new buffer is edited,
|
||||
// based on the value of 'fileformats' or 'fileencodings'. To prevent unexpected file conversion, we treat them as
|
||||
// local-noglobal. See `:help local-noglobal`, `:help 'fileformats'` and `:help 'fileencodings'`
|
||||
val bomb: ToggleOption =
|
||||
addOption(ToggleOption("bomb", LOCAL_TO_BUFFER, "bomb", false, isLocalNoGlobal = true))
|
||||
val fileencoding: StringOption = addOption(
|
||||
StringOption(
|
||||
"fileencoding",
|
||||
LOCAL_TO_BUFFER,
|
||||
"fenc",
|
||||
VimString.EMPTY,
|
||||
isLocalNoGlobal = true
|
||||
)
|
||||
)
|
||||
val fileformat: StringOption = addOption(
|
||||
StringOption(
|
||||
"fileformat",
|
||||
LOCAL_TO_BUFFER,
|
||||
"ff",
|
||||
if (injector.systemInfoService.isWindows) "dos" else "unix",
|
||||
boundedValues = setOf("dos", "unix", "mac"),
|
||||
isLocalNoGlobal = true
|
||||
)
|
||||
)
|
||||
|
||||
// IntelliJ specific functionality - custom options
|
||||
val ide: StringOption = addOption(
|
||||
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
||||
)
|
||||
public val ideacopypreprocess: ToggleOption = addOption(
|
||||
val ideacopypreprocess: ToggleOption = addOption(
|
||||
ToggleOption("ideacopypreprocess", GLOBAL_OR_LOCAL_TO_BUFFER, "ideacopypreprocess", false)
|
||||
)
|
||||
public val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
||||
public val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
||||
public val idearefactormode: StringOption = addOption(
|
||||
val ideajoin: ToggleOption = addOption(ToggleOption("ideajoin", GLOBAL_OR_LOCAL_TO_BUFFER, "ideajoin", false))
|
||||
val ideamarks: ToggleOption = addOption(ToggleOption("ideamarks", GLOBAL, "ideamarks", true))
|
||||
val idearefactormode: StringOption = addOption(
|
||||
StringOption(
|
||||
"idearefactormode",
|
||||
GLOBAL_OR_LOCAL_TO_BUFFER,
|
||||
@@ -52,7 +104,7 @@ public object IjOptions {
|
||||
IjOptionConstants.ideaRefactorModeValues
|
||||
)
|
||||
)
|
||||
public val ideastatusicon: StringOption = addOption(
|
||||
val ideastatusicon: StringOption = addOption(
|
||||
StringOption(
|
||||
"ideastatusicon",
|
||||
GLOBAL,
|
||||
@@ -61,7 +113,7 @@ public object IjOptions {
|
||||
IjOptionConstants.ideaStatusIconValues
|
||||
)
|
||||
)
|
||||
public val ideavimsupport: StringListOption = addOption(
|
||||
val ideavimsupport: StringListOption = addOption(
|
||||
StringListOption(
|
||||
"ideavimsupport",
|
||||
GLOBAL,
|
||||
@@ -70,24 +122,26 @@ public object IjOptions {
|
||||
IjOptionConstants.ideavimsupportValues
|
||||
)
|
||||
)
|
||||
@JvmField public val ideawrite: StringOption = addOption(
|
||||
@JvmField
|
||||
val ideawrite: StringOption = addOption(
|
||||
StringOption("ideawrite", GLOBAL, "ideawrite", "all", IjOptionConstants.ideaWriteValues)
|
||||
)
|
||||
public val lookupkeys: StringListOption = addOption(
|
||||
val lookupkeys: StringListOption = addOption(
|
||||
StringListOption(
|
||||
"lookupkeys",
|
||||
GLOBAL,
|
||||
"lookupkeys",
|
||||
"<Tab>,<Down>,<Up>,<Enter>,<Left>,<Right>,<C-Down>,<C-Up>,<PageUp>,<PageDown>,<C-J>,<C-Q>")
|
||||
)
|
||||
public val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true))
|
||||
public val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isTemporary = true))
|
||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isTemporary = true))
|
||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isTemporary = true))
|
||||
public val showmodewidget: ToggleOption = addOption(ToggleOption("showmodewidget", GLOBAL, "showmodewidget", false, isTemporary = true))
|
||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isTemporary = true))
|
||||
val trackactionids: ToggleOption = addOption(ToggleOption("trackactionids", GLOBAL, "tai", false))
|
||||
val visualdelay: UnsignedNumberOption = addOption(UnsignedNumberOption("visualdelay", GLOBAL, "visualdelay", 100))
|
||||
|
||||
// Temporary feature flags during development, not really intended for external use
|
||||
val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||
val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||
val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||
val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||
val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||
|
||||
// This needs to be Option<out VimDataType> so that it can work with derived option types, such as NumberOption, which
|
||||
// derives from Option<VimInt>
|
||||
|
@@ -15,38 +15,38 @@ import com.maddyhome.idea.vim.statistic.VimscriptState
|
||||
internal class IjStatisticsService : VimStatistics {
|
||||
|
||||
override fun logTrackedAction(actionId: String) {
|
||||
ActionTracker.logTrackedAction(actionId)
|
||||
ActionTracker.Util.logTrackedAction(actionId)
|
||||
}
|
||||
|
||||
override fun logCopiedAction(actionId: String) {
|
||||
ActionTracker.logCopiedAction(actionId)
|
||||
ActionTracker.Util.logCopiedAction(actionId)
|
||||
}
|
||||
|
||||
override fun setIfLoopUsed(value: Boolean) {
|
||||
VimscriptState.isLoopUsed = value
|
||||
VimscriptState.Util.isLoopUsed = value
|
||||
}
|
||||
|
||||
override fun setIfMapExprUsed(value: Boolean) {
|
||||
VimscriptState.isMapExprUsed = value
|
||||
VimscriptState.Util.isMapExprUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionCallUsed(value: Boolean) {
|
||||
VimscriptState.isFunctionCallUsed = value
|
||||
VimscriptState.Util.isFunctionCallUsed = value
|
||||
}
|
||||
|
||||
override fun setIfFunctionDeclarationUsed(value: Boolean) {
|
||||
VimscriptState.isFunctionDeclarationUsed = value
|
||||
VimscriptState.Util.isFunctionDeclarationUsed = value
|
||||
}
|
||||
|
||||
override fun setIfIfUsed(value: Boolean) {
|
||||
VimscriptState.isIfUsed = value
|
||||
VimscriptState.Util.isIfUsed = value
|
||||
}
|
||||
|
||||
override fun addExtensionEnabledWithPlug(extension: String) {
|
||||
VimscriptState.extensionsEnabledWithPlug.add(extension)
|
||||
VimscriptState.Util.extensionsEnabledWithPlug.add(extension)
|
||||
}
|
||||
|
||||
override fun addSourcedFile(path: String) {
|
||||
VimscriptState.sourcedFiles.add(path)
|
||||
VimscriptState.Util.sourcedFiles.add(path)
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.lang.CodeDocumentationAwareCommenter
|
||||
import com.intellij.lang.LanguageCommenters
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimPsiService
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.PsiHelper
|
||||
import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
|
||||
@Service
|
||||
class IjVimPsiService: VimPsiService {
|
||||
override fun getCommentAtPos(editor: VimEditor, pos: Int): Pair<TextRange, Pair<String, String>?>? {
|
||||
val psiFile = PsiHelper.getFile(editor.ij) ?: return null
|
||||
val psiElement = psiFile.findElementAt(pos) ?: return null
|
||||
val language = psiElement.language
|
||||
val commenter = LanguageCommenters.INSTANCE.forLanguage(language)
|
||||
val psiComment = PsiTreeUtil.getParentOfType(psiElement, PsiComment::class.java, false) ?: return null
|
||||
val commentText = psiComment.text
|
||||
|
||||
val blockCommentPrefix = commenter.blockCommentPrefix
|
||||
val blockCommentSuffix = commenter.blockCommentSuffix
|
||||
|
||||
val docCommentPrefix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentPrefix
|
||||
val docCommentSuffix = (commenter as? CodeDocumentationAwareCommenter)?.documentationCommentSuffix
|
||||
|
||||
val prefixToSuffix: Pair<String, String>? =
|
||||
if (docCommentPrefix != null && docCommentSuffix != null && commentText.startsWith(docCommentPrefix) && commentText.endsWith(docCommentSuffix)) {
|
||||
docCommentPrefix to docCommentSuffix
|
||||
}
|
||||
else if (blockCommentPrefix != null && blockCommentSuffix != null && commentText.startsWith(blockCommentPrefix) && commentText.endsWith(blockCommentSuffix)) {
|
||||
blockCommentPrefix to blockCommentSuffix
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
return Pair(psiComment.textRange.vim, prefixToSuffix)
|
||||
}
|
||||
|
||||
override fun getDoubleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
|
||||
// TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
|
||||
return getDoubleQuotesRangeNoPSI(editor.text(), pos, isInner)
|
||||
}
|
||||
|
||||
override fun getSingleQuotedString(editor: VimEditor, pos: Int, isInner: Boolean): TextRange? {
|
||||
// TODO[ideavim] It wasn't implemented before, but implementing it will significantly improve % motion
|
||||
return getSingleQuotesRangeNoPSI(editor.text(), pos, isInner)
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2003-2024 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimRedrawService
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
|
||||
class IjVimRedrawService : VimRedrawService {
|
||||
override fun redraw() {
|
||||
// The only thing IntelliJ needs to redraw is the status line. Everything else is handled automatically.
|
||||
redrawStatusLine()
|
||||
}
|
||||
|
||||
override fun redrawStatusLine() {
|
||||
injector.messages.clearStatusBarMessage()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Simulate Vim's redraw when the current editor changes
|
||||
*/
|
||||
fun fileEditorManagerSelectionChangedCallback(event: FileEditorManagerEvent) {
|
||||
injector.redrawService.redraw()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates Vim's redraw when the document changes
|
||||
*
|
||||
* Only redraw if lines are added/removed.
|
||||
*/
|
||||
object RedrawListener : DocumentListener {
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
if (VimPlugin.isNotEnabled()) return
|
||||
if (event.newFragment.contains("\n") || event.oldFragment.contains("\n")) {
|
||||
injector.redrawService.redraw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,10 +26,12 @@ import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
|
||||
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.api.NativeAction;
|
||||
import com.maddyhome.idea.vim.api.VimEditor;
|
||||
import com.maddyhome.idea.vim.api.VimInjectorKt;
|
||||
import com.maddyhome.idea.vim.api.VimKeyGroupBase;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.HelperKt;
|
||||
import com.maddyhome.idea.vim.key.*;
|
||||
import com.maddyhome.idea.vim.newapi.IjNativeAction;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
@@ -99,9 +101,9 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
|
||||
@Override
|
||||
public void updateShortcutKeysRegistration() {
|
||||
for (Editor editor : HelperKt.localEditors()) {
|
||||
unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
registerRequiredShortcutKeys(new IjVimEditor(editor));
|
||||
for (VimEditor editor : injector.getEditorGroup().getEditors()) {
|
||||
unregisterShortcutKeys(editor);
|
||||
registerRequiredShortcutKeys(editor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +230,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
|
||||
for (KeyStroke key : keys) {
|
||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
||||
if (!injector.getOptionGroup().getGlobalOptions().getOctopushandler() ||
|
||||
if (!injector.getApplication().isOctopusEnabled() ||
|
||||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
|
||||
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
|
||||
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
|
||||
|
@@ -10,6 +10,7 @@ package com.maddyhome.idea.vim.group
|
||||
import com.intellij.codeInsight.completion.CompletionPhase
|
||||
import com.intellij.codeInsight.completion.impl.CompletionServiceImpl
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
@@ -26,6 +27,7 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
/**
|
||||
* Used to handle playback of macros
|
||||
*/
|
||||
@Service
|
||||
internal class MacroGroup : VimMacroBase() {
|
||||
|
||||
// If it's null, this is the top macro (as in most cases). If it's not null, this macro is executed from top macro
|
||||
@@ -76,11 +78,12 @@ internal class MacroGroup : VimMacroBase() {
|
||||
} catch (e: ProcessCanceledException) {
|
||||
return@runnable
|
||||
}
|
||||
val keyHandler = getInstance()
|
||||
ProgressManager.getInstance().executeNonCancelableSection {
|
||||
// Prevent autocompletion during macros.
|
||||
// See https://github.com/JetBrains/ideavim/pull/772 for details
|
||||
CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion)
|
||||
getInstance().handleKey(editor, key, context)
|
||||
keyHandler.handleKey(editor, key, context, keyHandler.keyHandlerState)
|
||||
}
|
||||
if (injector.messages.isError()) return@runnable
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user