mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
645 Commits
customized
...
customized
Author | SHA1 | Date | |
---|---|---|---|
30de641513
|
|||
d1b58c9d39
|
|||
45ed79d865
|
|||
527eb4cbb3
|
|||
e32ac125b2
|
|||
4d1d3b697c
|
|||
3246832528
|
|||
6505bfc9aa
|
|||
0c63890e9d
|
|||
259958e702
|
|||
4916545b53
|
|||
b8a9bddfa9
|
|||
95688b33c8
|
|||
07f44f1c93
|
|||
2ce6239ad6
|
|||
a0d2d64237
|
|||
2e4e8c058b
|
|||
f464d25844
|
|||
acc12c5b17
|
|||
0c1bbd5e92
|
|||
f330e220ad
|
|||
![]() |
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 | ||
![]() |
f4cf06a50e | ||
![]() |
86bf8dcc60 | ||
![]() |
d37898b6d3 | ||
![]() |
1edd6a9002 | ||
![]() |
f7fa0dcbd1 | ||
![]() |
4f0a95a803 | ||
![]() |
e443cb0d3c | ||
![]() |
6fa228ee08 | ||
![]() |
fb9bfbaeeb | ||
![]() |
09668f4fcb | ||
![]() |
4c7a6165ed | ||
![]() |
12d0d2613f | ||
![]() |
42ee78cd3d | ||
![]() |
58d308c1ed | ||
![]() |
29e1bcc53d | ||
![]() |
3531574e5e | ||
![]() |
b9721218ab | ||
![]() |
a119ea6a29 | ||
![]() |
95ef5f5f32 | ||
![]() |
b81b18645b | ||
![]() |
ce591f1b43 | ||
![]() |
28afa4b3ce | ||
![]() |
89a24d71a6 | ||
f69630b668 | |||
![]() |
a2d34a883b | ||
![]() |
5c79b887d8 | ||
![]() |
d0475bf659 | ||
![]() |
85c9576699 | ||
![]() |
2483450a1f | ||
![]() |
519d5eed06 | ||
![]() |
d87965775a | ||
![]() |
8c6f81aa00 | ||
![]() |
6ea0ab0968 | ||
![]() |
70ab3ecdbe | ||
![]() |
a24ae616df | ||
![]() |
cc838f614f | ||
![]() |
ae62a9f378 | ||
![]() |
1b5778a58c | ||
![]() |
27a689e7b8 | ||
![]() |
8e6c490c62 | ||
![]() |
ccda70fe53 | ||
![]() |
26c42e4f0d | ||
![]() |
3244dd52eb | ||
![]() |
4c6807a0c2 | ||
![]() |
03a6a2749a | ||
![]() |
82f69456e9 | ||
![]() |
6beda371fb | ||
![]() |
5b9cb2efc5 | ||
![]() |
733968723c | ||
![]() |
63c81d67f2 | ||
![]() |
ad8ba1dd24 | ||
![]() |
04f821e3e1 | ||
![]() |
4937985e2c | ||
![]() |
5fd7d83a70 | ||
![]() |
699a19d202 | ||
![]() |
0b42938197 | ||
![]() |
1e2bfb6216 | ||
![]() |
f755a4b23f | ||
![]() |
4f58e12fca | ||
![]() |
588cf89531 | ||
![]() |
4fe2dd2706 | ||
![]() |
11ad605cd6 | ||
![]() |
fa9f160bd1 | ||
![]() |
dae1fad54e | ||
![]() |
52200188d4 | ||
![]() |
0d74b9ef0b | ||
![]() |
549163d274 | ||
![]() |
755018c783 | ||
![]() |
2a1c4b3a1c | ||
![]() |
aae0d825e7 | ||
![]() |
855dbfab16 | ||
![]() |
f3a357c559 | ||
![]() |
63995e8c61 | ||
![]() |
7062d9b8f8 | ||
![]() |
ede62f5c75 | ||
![]() |
6386770ff3 | ||
![]() |
b4e831a81f | ||
![]() |
9b283360ce | ||
![]() |
fabbd4d156 | ||
![]() |
9bea5bf5f7 | ||
![]() |
9fbc990493 | ||
![]() |
b05fdaaa73 | ||
![]() |
52d5d4d64c | ||
![]() |
6ec712466c | ||
![]() |
6616b8dc07 | ||
![]() |
807457c718 | ||
![]() |
13d2a40903 | ||
![]() |
022b196d6a | ||
![]() |
7a64216830 | ||
![]() |
bf7d2bd465 | ||
![]() |
6e97b591de | ||
![]() |
fc7c470966 | ||
![]() |
51492ca121 | ||
![]() |
ce1df84330 | ||
![]() |
9b43e2a715 | ||
![]() |
732cabd6aa | ||
![]() |
7c14801d5c | ||
![]() |
66df09c065 | ||
![]() |
8fd6985316 | ||
![]() |
feac001499 | ||
![]() |
4c47e3a8eb | ||
![]() |
7773b625a5 | ||
![]() |
abe1abec72 | ||
![]() |
023838a96b | ||
![]() |
f4e743acc5 | ||
![]() |
06d58cbda5 | ||
![]() |
d199dea204 | ||
![]() |
5722060ed9 | ||
![]() |
d4f7e727c1 | ||
![]() |
ba9afc3f8e | ||
![]() |
39897bd012 | ||
![]() |
575d563154 | ||
![]() |
2bf46ce2f3 | ||
![]() |
b49a185efc | ||
![]() |
e305ebd1ed | ||
![]() |
6f5c9826f4 | ||
![]() |
6025eaaca9 | ||
![]() |
b2441c3cca | ||
![]() |
a73599e9ee | ||
![]() |
58398f40fa | ||
![]() |
43f5d5a8e8 | ||
![]() |
b20cbd3558 | ||
![]() |
7f835a407c | ||
![]() |
9859974db7 | ||
![]() |
6c24ddd1a0 | ||
![]() |
bd92ef08ec | ||
![]() |
8de6107a17 | ||
![]() |
e639f03ac7 | ||
![]() |
f9aac442c1 | ||
![]() |
5fdf675168 | ||
![]() |
232f81ff48 | ||
![]() |
1c4a6b2274 | ||
![]() |
deb71f8efc | ||
![]() |
4596596d9f | ||
![]() |
bbb6d42f8d | ||
![]() |
01efd0f9f0 | ||
![]() |
2d7597d206 | ||
![]() |
221741c891 | ||
![]() |
9f69beb450 | ||
![]() |
e843d9e9c3 | ||
![]() |
008b3d94fb | ||
![]() |
6756d83c55 | ||
![]() |
b52072a2e3 | ||
![]() |
3afb00d563 | ||
![]() |
a30c94fd2f | ||
![]() |
f50c29a285 | ||
![]() |
f238b0f138 | ||
![]() |
d0a8c98040 | ||
![]() |
b3d161ad97 | ||
![]() |
fce9cf2077 | ||
![]() |
efd0e56697 | ||
![]() |
b94a9bb9d9 | ||
![]() |
c153cc5a29 | ||
![]() |
a680e9a25a | ||
![]() |
3c18c4ef22 | ||
![]() |
c4e11b5976 | ||
![]() |
65be51dd48 | ||
![]() |
9684103f97 | ||
![]() |
f4c647d430 | ||
![]() |
f1eab3b9c1 | ||
![]() |
545d52bd93 | ||
![]() |
4e42198c09 | ||
![]() |
44736a51b9 | ||
![]() |
e675ffd623 | ||
![]() |
1f14e06bd3 | ||
![]() |
9871078269 | ||
![]() |
5e7a7f4d62 | ||
![]() |
7d690c6809 | ||
![]() |
6edb4266d5 | ||
![]() |
799e82d501 | ||
![]() |
a2370bff68 | ||
![]() |
c72f3bcd12 | ||
![]() |
295964a74d | ||
![]() |
d77cda0fae | ||
![]() |
6da072d47d | ||
![]() |
471a5a1b3e | ||
![]() |
cd5da2d237 | ||
![]() |
62f67cd626 | ||
![]() |
70db96d9e5 | ||
![]() |
98470111fb | ||
![]() |
557a3bb01f | ||
![]() |
dee70acdcb | ||
![]() |
862b16879c | ||
![]() |
ed7249558e | ||
![]() |
4f6c6f4d10 | ||
![]() |
650d02d9b3 | ||
![]() |
e4041a2f69 | ||
![]() |
4c284a6d13 | ||
![]() |
e14fc801bd | ||
![]() |
0478d468e0 | ||
![]() |
4ac98710fb | ||
![]() |
f256f6417e | ||
![]() |
ca94d55b62 | ||
![]() |
c11c061113 | ||
![]() |
c15c3eb802 | ||
![]() |
0ce102b782 | ||
![]() |
cc48207a99 | ||
![]() |
353ea5fc5d | ||
![]() |
64138310cc | ||
![]() |
1c4538af72 | ||
![]() |
755b47ef19 | ||
![]() |
c78a5d3cab | ||
![]() |
b9b8d30f3b | ||
![]() |
9be93212c3 | ||
![]() |
89973809af | ||
![]() |
e324af356d | ||
![]() |
f51fc6ed47 | ||
![]() |
ecce98289a | ||
![]() |
23c14aa2e4 | ||
![]() |
678d04c5db | ||
![]() |
691ba75372 | ||
![]() |
d2d7bbc632 | ||
![]() |
b3b1a6bdb9 | ||
![]() |
310125ea01 | ||
![]() |
208d1cbba2 | ||
![]() |
e94154ba80 | ||
![]() |
582fbdd9e7 | ||
![]() |
dd175912f4 | ||
![]() |
a6a0ae7a51 | ||
![]() |
8cdac91a01 | ||
![]() |
4c89f41daa | ||
![]() |
512e826a42 | ||
![]() |
bc0d277a21 | ||
![]() |
169fe5fc5b | ||
![]() |
30867702a4 | ||
![]() |
6131f92ae6 | ||
![]() |
823a52583c | ||
![]() |
e2c6c0539f | ||
![]() |
f7f1c0e90d | ||
![]() |
eca12607dd | ||
![]() |
006e3e11f9 | ||
![]() |
a9982cbdca | ||
![]() |
0fa9c5a2a2 | ||
![]() |
cdcc9729d3 | ||
![]() |
4acf651aa7 | ||
![]() |
4bba791c65 | ||
![]() |
662688d3b9 | ||
![]() |
21a3e8fdc4 | ||
![]() |
3815a1d538 | ||
![]() |
cbe0c5cfec | ||
![]() |
15db9b30e1 | ||
![]() |
e891294c0f | ||
![]() |
f6b9e7cc26 | ||
![]() |
052fd7162f | ||
![]() |
189acb73f5 | ||
![]() |
ec7c1677b4 | ||
![]() |
a9474c8e67 | ||
![]() |
3a70dfc5f3 | ||
![]() |
669177d803 | ||
![]() |
b1f43b061c | ||
![]() |
7ff3c84deb | ||
![]() |
ee642b63ce | ||
![]() |
17315e5096 | ||
![]() |
4e9d52fc62 | ||
![]() |
d7e87f8fc8 | ||
![]() |
3efe11f393 | ||
![]() |
26c6c464d8 | ||
![]() |
4db654e653 | ||
![]() |
048759d374 | ||
![]() |
db2424057f | ||
![]() |
472a53e3b9 | ||
![]() |
9e15d91900 | ||
![]() |
d5cff281c0 | ||
![]() |
57b6c4dffb | ||
![]() |
908a2d1d8c | ||
![]() |
69bdea9273 | ||
![]() |
5b21a653ee | ||
![]() |
cfddcf1630 | ||
![]() |
f009687ddf | ||
![]() |
6ddfe29465 | ||
![]() |
715c51f673 | ||
![]() |
b443e8f06a | ||
![]() |
0bd0466c9c | ||
![]() |
ad5db3c9e5 | ||
![]() |
fa3182cb5e | ||
![]() |
3f44bed66e | ||
![]() |
2a70530d0f | ||
![]() |
7c542d5fc7 | ||
![]() |
638dfb7777 | ||
![]() |
1323536a63 | ||
![]() |
419212e2d4 | ||
![]() |
5f1c234a7d | ||
![]() |
db1e8301cd | ||
![]() |
bf94a3c68d | ||
![]() |
96baa4ffc6 | ||
![]() |
7d472afe61 | ||
![]() |
f32a4d33a7 | ||
![]() |
0722991955 | ||
![]() |
bcc740cdbc | ||
![]() |
5cf46097f7 | ||
![]() |
61dc189f8b | ||
![]() |
23c2b008c9 | ||
![]() |
db14afdf3a | ||
![]() |
b7927336d1 | ||
![]() |
ee23a3d4cd | ||
![]() |
63c0112ffb | ||
![]() |
db08d7d280 | ||
![]() |
9892525fbc | ||
![]() |
34b87ff6bf | ||
![]() |
241ad68bd5 | ||
![]() |
a0ec18921b | ||
![]() |
45e17eb0b2 | ||
![]() |
59f0e9ae67 | ||
![]() |
af24611c73 | ||
![]() |
d4502dda3f | ||
![]() |
c0efa8af5d | ||
![]() |
1c06a3fc89 | ||
![]() |
c19fb38d1c | ||
![]() |
5dc1de9daf | ||
![]() |
6774301938 | ||
![]() |
4ef6cf0428 | ||
![]() |
ca5f8e4b44 | ||
![]() |
1907f03abe | ||
![]() |
6351a4e4f3 | ||
![]() |
fa34c3937f | ||
![]() |
cdac97ebf5 | ||
![]() |
fe958d28b8 | ||
![]() |
f71982e1d5 | ||
![]() |
cb2bfcea53 | ||
![]() |
4a9d5bbceb | ||
![]() |
10809eade6 | ||
![]() |
43d63527f8 | ||
![]() |
df51eb54ed | ||
![]() |
b47109ab4d | ||
![]() |
15b2b68940 | ||
![]() |
62a239f6fe | ||
![]() |
d89bc95a0a | ||
![]() |
2a76f21b31 | ||
![]() |
f07e22d742 | ||
![]() |
058ab7a1ea | ||
![]() |
fae3baa640 | ||
![]() |
2c4da9c634 | ||
![]() |
8de0313aca | ||
![]() |
143c5b17f9 | ||
![]() |
ec32fde60d | ||
![]() |
f2ac5d4995 | ||
![]() |
716962af03 | ||
![]() |
156efde6b9 | ||
![]() |
a9b7716dfe | ||
![]() |
76a67a6715 | ||
![]() |
c3defdcda4 | ||
![]() |
d8092aa916 | ||
![]() |
8b5a3d31aa | ||
![]() |
11761b66b2 | ||
![]() |
f83f107bd1 | ||
![]() |
f1b90857ff | ||
![]() |
5aea4cdd65 | ||
![]() |
1822a59c70 | ||
![]() |
37c6934802 | ||
![]() |
90c7f747a4 | ||
![]() |
b7efa3dcd6 | ||
![]() |
da80f537ac | ||
![]() |
0119912318 | ||
![]() |
5e0b1d0161 | ||
![]() |
35145d100b | ||
![]() |
584dd0ba89 | ||
![]() |
e5f5dc56c9 | ||
![]() |
880efb012a | ||
![]() |
b95308ac24 | ||
![]() |
3b192ad357 | ||
![]() |
b04938ac5e | ||
![]() |
56410ac1f2 | ||
![]() |
45a2eadc58 | ||
![]() |
0e03151505 | ||
![]() |
3e9706e6ce | ||
![]() |
a1646a7a88 | ||
![]() |
434511658b | ||
![]() |
04230fdd9c | ||
![]() |
2e16ad8a2a | ||
![]() |
7fb59b0fa9 | ||
![]() |
24e044bcda | ||
![]() |
1093656ec5 | ||
![]() |
674e997060 | ||
![]() |
37fd124f56 | ||
![]() |
7df2e67312 | ||
![]() |
8ea1f0796c | ||
![]() |
00fb5bc6cf | ||
![]() |
5e01f726d3 | ||
![]() |
e87290aeea | ||
![]() |
e7236beedd | ||
![]() |
5a6f54c96c | ||
![]() |
7769985439 | ||
![]() |
f4afdb21b2 | ||
![]() |
cc1b9e0a50 | ||
![]() |
2c58740cbb | ||
![]() |
808533b110 | ||
![]() |
e04a15bb99 | ||
![]() |
26d4074a61 | ||
![]() |
0137de5ca2 | ||
![]() |
b0a1b2edba | ||
![]() |
355c560ddc | ||
![]() |
72f286d9c6 | ||
![]() |
db6786414a | ||
![]() |
f8f046f193 | ||
![]() |
6c9ad4ded2 | ||
![]() |
32cae8ca11 | ||
![]() |
0cb65279d9 | ||
![]() |
412da06554 | ||
![]() |
247f8a2778 | ||
![]() |
017c9a6a70 | ||
![]() |
eccb2430b5 | ||
![]() |
5c64ebf1cc | ||
![]() |
1d7796805c | ||
![]() |
3479aaf6f6 | ||
![]() |
d2071cf05c |
2
.github/workflows/mergePr.yml
vendored
2
.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:
|
||||
|
81
.github/workflows/runUiOctopusTests.yml
vendored
Normal file
81
.github/workflows/runUiOctopusTests.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Run Non Octopus UI Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
jobs:
|
||||
build-for-ui-test-mac-os:
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
run: gradle :buildPlugin
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle runIdeForUiTests -Doctopus.handler=false > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
url: http://127.0.0.1:8082
|
||||
max-attempts: 20
|
||||
retry-delay: 10s
|
||||
- name: Tests
|
||||
run: gradle :tests:ui-ij-tests:testUi
|
||||
- name: Move video
|
||||
if: always()
|
||||
run: mv tests/ui-ij-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
- name: Save report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
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
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Setup Java
|
||||
# uses: actions/setup-java@v2.1.0
|
||||
# with:
|
||||
# distribution: zulu
|
||||
# java-version: 11
|
||||
# - name: Build Plugin
|
||||
# run: gradle :buildPlugin
|
||||
# - name: Run Idea
|
||||
# run: |
|
||||
# export DISPLAY=:99.0
|
||||
# Xvfb -ac :99 -screen 0 1920x1080x16 &
|
||||
# mkdir -p build/reports
|
||||
# gradle :runIdeForUiTests #> build/reports/idea.log
|
||||
# - name: Wait for Idea started
|
||||
# uses: jtalk/url-health-check-action@1.5
|
||||
# with:
|
||||
# url: http://127.0.0.1:8082
|
||||
# max-attempts: 15
|
||||
# retry-delay: 30s
|
||||
# - name: Tests
|
||||
# run: gradle :testUi
|
||||
# - name: Save fails report
|
||||
# if: ${{ failure() }}
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: ui-test-fails-report-linux
|
||||
# path: |
|
||||
# ui-test-example/build/reports
|
52
.github/workflows/runUiPyTests.yml
vendored
Normal file
52
.github/workflows/runUiPyTests.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Run UI PyCharm Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
jobs:
|
||||
build-for-ui-test-mac-os:
|
||||
if: github.repository == 'JetBrains/ideavim'
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 17
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Setup FFmpeg
|
||||
run: brew install ffmpeg
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
- name: Build Plugin
|
||||
run: gradle :buildPlugin
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle :runIdeForUiTests -PideaType=PC > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
url: http://127.0.0.1:8082
|
||||
max-attempts: 20
|
||||
retry-delay: 10s
|
||||
- name: Tests
|
||||
run: gradle :tests:ui-py-tests:testUi
|
||||
- name: Move video
|
||||
if: always()
|
||||
run: mv tests/ui-py-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
- name: Save report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ui-test-fails-report-mac
|
||||
path: |
|
||||
build/reports
|
||||
tests/ui-py-tests/build/reports
|
||||
sandbox-idea-log
|
15
.github/workflows/runUiTests.yml
vendored
15
.github/workflows/runUiTests.yml
vendored
@@ -13,13 +13,9 @@ jobs:
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 11
|
||||
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
|
||||
@@ -27,7 +23,7 @@ jobs:
|
||||
- name: Run Idea
|
||||
run: |
|
||||
mkdir -p build/reports
|
||||
gradle :runIdeForUiTests > build/reports/idea.log &
|
||||
gradle runIdeForUiTests > build/reports/idea.log &
|
||||
- name: Wait for Idea started
|
||||
uses: jtalk/url-health-check-action@v3
|
||||
with:
|
||||
@@ -35,10 +31,10 @@ jobs:
|
||||
max-attempts: 20
|
||||
retry-delay: 10s
|
||||
- name: Tests
|
||||
run: gradle :testUi
|
||||
run: gradle :tests:ui-ij-tests:testUi
|
||||
- name: Move video
|
||||
if: always()
|
||||
run: mv video build/reports
|
||||
run: mv tests/ui-ij-tests/video build/reports
|
||||
- name: Move sandbox logs
|
||||
if: always()
|
||||
run: mv build/idea-sandbox/system/log sandbox-idea-log
|
||||
@@ -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 }}
|
5
.github/workflows/updateChangelog.yml
vendored
5
.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
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,6 +23,7 @@
|
||||
|
||||
# Generated by gradle task "generateGrammarSource"
|
||||
src/main/java/com/maddyhome/idea/vim/vimscript/parser/generated
|
||||
vim-engine/src/main/java/com/maddyhome/idea/vim/regexp/parser/generated
|
||||
# Generated JSONs for lazy classloading
|
||||
/vim-engine/src/main/resources/ksp-generated
|
||||
/src/main/resources/ksp-generated
|
||||
|
3
.teamcity/_Self/Project.kt
vendored
3
.teamcity/_Self/Project.kt
vendored
@@ -15,7 +15,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
||||
|
||||
object Project : Project({
|
||||
description = "Vim engine for IDEs based on the IntelliJ platform"
|
||||
description = "Vim engine for JetBrains IDEs"
|
||||
|
||||
subProjects(Releases, OldTests, GitHub)
|
||||
|
||||
@@ -25,6 +25,7 @@ object Project : Project({
|
||||
// Active tests
|
||||
buildType(TestingBuildType("Latest EAP", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
buildType(TestingBuildType("2023.3", "<default>", version = "2023.3"))
|
||||
buildType(TestingBuildType("2024.1", "<default>"))
|
||||
buildType(TestingBuildType("Latest EAP With Xorg", "<default>", version = "LATEST-EAP-SNAPSHOT"))
|
||||
|
||||
buildType(PropertyBased)
|
||||
|
1
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
1
.teamcity/_Self/buildTypes/Compatibility.kt
vendored
@@ -34,7 +34,6 @@ object Compatibility : IdeaVimBuildType({
|
||||
|
||||
java --version
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}org.jetbrains.IdeaVim-EasyMotion' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}io.github.mishkun.ideavimsneak' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}eu.theblob42.idea.whichkey' [latest-IU] -team-city
|
||||
java -jar verifier1/verifier-cli-dev-all-1.jar check-plugin '${'$'}IdeaVimExtension' [latest-IU] -team-city
|
||||
# Outdated java -jar verifier/verifier-cli-dev-all.jar check-plugin '${'$'}github.zgqq.intellij-enhance' [latest-IU] -team-city
|
||||
|
2
.teamcity/_Self/buildTypes/LongRunning.kt
vendored
2
.teamcity/_Self/buildTypes/LongRunning.kt
vendored
@@ -25,7 +25,7 @@ object LongRunning : IdeaVimBuildType({
|
||||
|
||||
steps {
|
||||
gradle {
|
||||
tasks = "clean testLongRunning"
|
||||
tasks = "clean :tests:long-running-tests:testLongRunning"
|
||||
buildFile = ""
|
||||
enableStacktrace = true
|
||||
}
|
||||
|
2
.teamcity/_Self/buildTypes/Nvim.kt
vendored
2
.teamcity/_Self/buildTypes/Nvim.kt
vendored
@@ -39,7 +39,7 @@ object Nvim : IdeaVimBuildType({
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
tasks = "clean testWithNeovim"
|
||||
tasks = "clean test -Dnvim"
|
||||
buildFile = ""
|
||||
enableStacktrace = true
|
||||
}
|
||||
|
2
.teamcity/_Self/buildTypes/PropertyBased.kt
vendored
2
.teamcity/_Self/buildTypes/PropertyBased.kt
vendored
@@ -24,7 +24,7 @@ object PropertyBased : IdeaVimBuildType({
|
||||
|
||||
steps {
|
||||
gradle {
|
||||
tasks = "clean testPropertyBased"
|
||||
tasks = "clean :tests:property-tests:testPropertyBased"
|
||||
buildFile = ""
|
||||
enableStacktrace = true
|
||||
}
|
||||
|
@@ -20,8 +20,8 @@ object PublishVimEngine : IdeaVimBuildType({
|
||||
params {
|
||||
param("env.ORG_GRADLE_PROJECT_engineVersion", "%build.number%")
|
||||
param("env.ORG_GRADLE_PROJECT_uploadUrl", "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
|
||||
password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:790b4e43-ee83-4184-b81b-678afab60409", display = ParameterDisplay.HIDDEN)
|
||||
param("env.ORG_GRADLE_PROJECT_spaceUsername", "Aleksei.Plate")
|
||||
password("env.ORG_GRADLE_PROJECT_spacePassword", "credentialsJSON:5ea56f8c-efe7-4e1e-83de-0c02bcc39d0b", display = ParameterDisplay.HIDDEN)
|
||||
param("env.ORG_GRADLE_PROJECT_spaceUsername", "a121c67e-39ac-40e6-bf82-649855ec27b6")
|
||||
}
|
||||
|
||||
vcs {
|
||||
|
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
|
||||
}
|
||||
}
|
||||
|
||||
|
20
.teamcity/_Self/buildTypes/ReleaseEap.kt
vendored
20
.teamcity/_Self/buildTypes/ReleaseEap.kt
vendored
@@ -5,6 +5,7 @@ import _Self.Constants.RELEASE_EAP
|
||||
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.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
|
||||
@@ -30,6 +31,11 @@ object ReleaseEap : IdeaVimBuildType({
|
||||
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
|
||||
label = "Slack Token"
|
||||
)
|
||||
password(
|
||||
"env.YOUTRACK_TOKEN",
|
||||
"credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622",
|
||||
display = ParameterDisplay.HIDDEN
|
||||
)
|
||||
}
|
||||
|
||||
vcs {
|
||||
@@ -61,26 +67,26 @@ object ReleaseEap : IdeaVimBuildType({
|
||||
tasks = "scripts:addReleaseTag"
|
||||
}
|
||||
gradle {
|
||||
name = "Publish plugin"
|
||||
tasks = "publishPlugin"
|
||||
}
|
||||
// Same as the script below. However, jgit can't find ssh keys on TeamCity
|
||||
// gradle {
|
||||
// name = "Push changes to the repo"
|
||||
// tasks = "scripts:pushChanges"
|
||||
// }
|
||||
script {
|
||||
name = "Push changes to the repo"
|
||||
scriptContent = """
|
||||
branch=$(git branch --show-current)
|
||||
branch=$(git branch --show-current)
|
||||
echo current branch is ${'$'}branch
|
||||
if [ "master" != "${'$'}branch" ];
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
git push origin %build.number%
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
name = "YouTrack post release actions"
|
||||
tasks = "scripts:eapReleaseActions"
|
||||
}
|
||||
}
|
||||
|
||||
features {
|
||||
|
91
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
91
.teamcity/_Self/buildTypes/ReleasePlugin.kt
vendored
@@ -45,7 +45,11 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
"credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5",
|
||||
label = "Slack Token"
|
||||
)
|
||||
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
|
||||
password(
|
||||
"env.ORG_GRADLE_PROJECT_youtrackToken",
|
||||
"credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3",
|
||||
display = ParameterDisplay.HIDDEN
|
||||
)
|
||||
param("env.ORG_GRADLE_PROJECT_releaseType", releaseType)
|
||||
}
|
||||
|
||||
@@ -65,9 +69,25 @@ sealed class ReleasePlugin(private val releaseType: String) : IdeaVimBuildType({
|
||||
name = "Pull git history"
|
||||
scriptContent = "git fetch --unshallow"
|
||||
}
|
||||
gradle {
|
||||
name = "Select branch"
|
||||
tasks = "scripts:selectBranch"
|
||||
script {
|
||||
name = "Reset release branch"
|
||||
scriptContent = """
|
||||
if [ "major" = "$releaseType" ] || [ "minor" = "$releaseType" ]
|
||||
then
|
||||
echo Resetting the release branch because the release type is $releaseType
|
||||
git checkout master
|
||||
latest_eap=${'$'}(git describe --tags --match="[0-9].[0-9]*.[0-9]-eap.[0-9]*" --abbrev=0 HEAD)
|
||||
echo Latest EAP: ${'$'}latest_eap
|
||||
commit_of_latest_eap=${'$'}(git rev-list -n 1 ${'$'}latest_eap)
|
||||
echo Commit of latest EAP: ${'$'}commit_of_latest_eap
|
||||
git checkout release
|
||||
git reset --hard ${'$'}commit_of_latest_eap
|
||||
else
|
||||
git checkout release
|
||||
echo Do not reset the release branch because the release type is $releaseType
|
||||
fi
|
||||
echo Checked out release branch
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
name = "Calculate new version"
|
||||
@@ -77,53 +97,50 @@ 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"
|
||||
}
|
||||
gradle {
|
||||
name = "Reset release branch"
|
||||
tasks = "scripts:resetReleaseBranch"
|
||||
script {
|
||||
name = "Run tests"
|
||||
scriptContent = "./gradlew test"
|
||||
}
|
||||
gradle {
|
||||
name = "Publish release"
|
||||
tasks = "publishPlugin"
|
||||
}
|
||||
// script {
|
||||
// name = "Checkout master branch"
|
||||
// scriptContent = """
|
||||
// echo Checkout master
|
||||
// git checkout master
|
||||
// """.trimIndent()
|
||||
// }
|
||||
// gradle {
|
||||
// name = "Push changes to the repo"
|
||||
// tasks = "scripts:pushChangesWithReleaseBranch"
|
||||
// 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 --tags
|
||||
git push origin
|
||||
|
||||
if [ "patch" != $releaseType ];
|
||||
then
|
||||
git checkout release
|
||||
echo checkout release branch
|
||||
git branch --set-upstream-to=origin/release release
|
||||
git push --tags
|
||||
git push origin --force
|
||||
fi
|
||||
|
||||
git checkout ${'$'}branch
|
||||
git checkout release
|
||||
echo checkout release branch
|
||||
git branch --set-upstream-to=origin/release release
|
||||
git push origin --force
|
||||
# Push tag
|
||||
git push origin %build.number%
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle {
|
||||
|
@@ -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"
|
||||
|
19
.teamcity/patches/buildTypes/PublishVimEngine.kts
vendored
Normal file
19
.teamcity/patches/buildTypes/PublishVimEngine.kts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
To apply the patch, change the buildType with id = 'PublishVimEngine'
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeBuildType(RelativeId("PublishVimEngine")) {
|
||||
vcs {
|
||||
|
||||
check(branchFilter == "+:<default>") {
|
||||
"Unexpected option value: branchFilter = $branchFilter"
|
||||
}
|
||||
branchFilter = "+:fleet"
|
||||
}
|
||||
}
|
20
.teamcity/patches/buildTypes/ReleaseMinor.kts
vendored
20
.teamcity/patches/buildTypes/ReleaseMinor.kts
vendored
@@ -1,20 +0,0 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
To apply the patch, change the buildType with id = 'ReleaseMinor'
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeBuildType(RelativeId("ReleaseMinor")) {
|
||||
params {
|
||||
expect {
|
||||
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
|
||||
}
|
||||
update {
|
||||
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
|
||||
}
|
||||
}
|
||||
}
|
20
.teamcity/patches/buildTypes/ReleasePatch.kts
vendored
20
.teamcity/patches/buildTypes/ReleasePatch.kts
vendored
@@ -1,20 +0,0 @@
|
||||
package patches.buildTypes
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.ui.*
|
||||
|
||||
/*
|
||||
This patch script was generated by TeamCity on settings change in UI.
|
||||
To apply the patch, change the buildType with id = 'ReleasePatch'
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeBuildType(RelativeId("ReleasePatch")) {
|
||||
params {
|
||||
expect {
|
||||
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:3cd3e867-282c-451f-b958-bc95d56a8450", display = ParameterDisplay.HIDDEN)
|
||||
}
|
||||
update {
|
||||
password("env.ORG_GRADLE_PROJECT_youtrackToken", "credentialsJSON:7bc0eb3a-b86a-4ebd-b622-d4ef12d7e1d3", display = ParameterDisplay.HIDDEN)
|
||||
}
|
||||
}
|
||||
}
|
17
.teamcity/patches/projects/_Self.kts
vendored
17
.teamcity/patches/projects/_Self.kts
vendored
@@ -1,17 +0,0 @@
|
||||
package patches.projects
|
||||
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.*
|
||||
import jetbrains.buildServer.configs.kotlin.v2019_2.Project
|
||||
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 root project
|
||||
accordingly, and delete the patch script.
|
||||
*/
|
||||
changeProject(DslContext.projectId) {
|
||||
check(description == "Vim engine for IDEs based on the IntelliJ platform") {
|
||||
"Unexpected description: '$description'"
|
||||
}
|
||||
description = "Vim engine for JetBrains IDEs"
|
||||
}
|
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.05"
|
||||
version = "2023.11"
|
||||
project(_Self.Project)
|
||||
|
12
AUTHORS.md
12
AUTHORS.md
@@ -491,6 +491,18 @@ Contributors:
|
||||
[![icon][github]](https://github.com/Infonautica)
|
||||
|
||||
Leonid Danilov
|
||||
* [![icon][mail]](mailto:emanuel-367@hotmail.com)
|
||||
[![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
|
||||
|
||||
Previous contributors:
|
||||
|
||||
|
19
CHANGES.md
19
CHANGES.md
@@ -23,7 +23,22 @@ 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
|
||||
|
||||
### Merged PRs:
|
||||
* [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
|
||||
|
||||
### Fixes:
|
||||
* [VIM-3130](https://youtrack.jetbrains.com/issue/VIM-3130) Change the build version to 2023.1.2
|
||||
@@ -44,6 +59,8 @@ usual beta standards.
|
||||
* [VIM-3206](https://youtrack.jetbrains.com/issue/VIM-3206) Disable both copilot suggestion and insert mode on a single escape
|
||||
* [VIM-3090](https://youtrack.jetbrains.com/issue/VIM-3090) Cmd line mode saves the visual mode
|
||||
* [VIM-3085](https://youtrack.jetbrains.com/issue/VIM-3085) Open access to VimTypedActionHandler and VimShortcutKeyAction
|
||||
* [VIM-3260](https://youtrack.jetbrains.com/issue/VIM-3260) Processing the offsets at the file end
|
||||
* [VIM-3183](https://youtrack.jetbrains.com/issue/VIM-3183) Execute .ideavimrc on pooled thread
|
||||
|
||||
### Merged PRs:
|
||||
* [763](https://github.com/JetBrains/ideavim/pull/763) by [Sam Ng](https://github.com/samabcde): Fix(VIM-3176) add test for restore selection after pasting in/below s…
|
||||
|
@@ -84,3 +84,8 @@ IV) It is not allowed to remove this license from the distribution of the Vim
|
||||
license for previous Vim releases instead of the license that they came
|
||||
with, at your option.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
File [sneakIcon.png](doc/images/sneakIcon.svg), which is originally an icon of the ideavim-sneak plugin,
|
||||
is merged icons of IdeaVim plugin and a random sneaker by FreePic from flaticon.com.
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization") version "1.8.21"
|
||||
kotlin("plugin.serialization") version "1.9.22"
|
||||
}
|
||||
|
||||
val kotlinxSerializationVersion: String by project
|
||||
@@ -21,7 +21,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.22-1.0.16")
|
||||
compileOnly("com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20")
|
||||
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")
|
||||
|
1389
build.gradle.kts
1389
build.gradle.kts
File diff suppressed because it is too large
Load Diff
@@ -44,16 +44,21 @@ All commands with the mappings are supported. See the [full list of supported co
|
||||
|
||||
<details>
|
||||
<summary><h2>sneak</h2></summary>
|
||||
|
||||
|
||||
<img src="images/sneakIcon.svg" width="80" height="80" alt="icon"/>
|
||||
|
||||
By [Mikhail Levchenko](https://github.com/Mishkun)
|
||||
Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
||||
Original plugin: [vim-sneak](https://github.com/justinmk/vim-sneak).
|
||||
|
||||
### Setup:
|
||||
- Install [IdeaVim-sneak](https://plugins.jetbrains.com/plugin/15348-ideavim-sneak) plugin.
|
||||
- Add the following command to `~/.ideavimrc`: `set sneak`
|
||||
- Add the following command to `~/.ideavimrc`: `Plug 'justinmk/vim-sneak'`
|
||||
|
||||
### Instructions
|
||||
|
||||
See the [docs](https://github.com/Mishkun/ideavim-sneak#usage)
|
||||
|
||||
* Type `s` and two chars to start sneaking in forward direction
|
||||
* Type `S` and two chars to start sneaking in backward direction
|
||||
* Type `;` or `,` to proceed with sneaking just as if you were using `f` or `t` commands
|
||||
|
||||
</details>
|
||||
|
||||
|
28
doc/images/sneakIcon.svg
Normal file
28
doc/images/sneakIcon.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="386.498 234 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
|
||||
<stop offset="0" stop-color="#3BEA62"/>
|
||||
<stop offset="1" stop-color="#087CFA"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="matrix(1.238978, 0.90017, -0.90017, 1.238978, 131.776901, -422.953003)" style="">
|
||||
<path d="M 399.962 247.648 C 399.207 246.894 399.147 246.318 399.692 245.453 C 400.237 244.588 401.955 245.886 401.955 245.886 L 401.955 250.737" style="fill: rgb(248, 245, 231);"/>
|
||||
<path d="M 413.846 253.602 C 413.846 255.077 411.827 256.134 409.392 256.134 L 396.381 256.134 C 395.211 256.134 394.232 256.003 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 413.846 253.602" fill="#cce6f6" style=""/>
|
||||
<path d="M 413.846 253.602 C 413.846 253.602 411.475 254.468 408.27 254.468 C 405.94 254.468 398.116 253.433 394.023 252.869 C 392.488 252.658 391.478 252.512 391.478 252.512 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 C 396.446 247.877 396.955 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 406.544 249.03 410.097 250.43 410.097 250.43 C 410.097 250.43 413.061 250.446 413.782 251.167 C 414.503 251.888 414.422 253.218 413.846 253.602 Z M 413.846 253.602" style="fill: rgb(8, 124, 250);"/>
|
||||
<path d="M 394.023 252.869 L 393.433 255.817 C 391.496 255.367 390.613 254.596 390.613 254.596 L 391.478 252.513 L 393.607 252.617 Z M 394.023 252.869" fill="#9dcae0" style=""/>
|
||||
<path d="M 396.059 247.225 C 395.073 245.986 393.193 250.255 394.023 252.869 C 392.488 252.658 391.478 252.513 391.478 252.513 C 390.864 251.662 392.078 248.741 392.758 247.263 C 393.118 246.484 393.85 245.929 394.703 245.83 C 394.782 245.82 394.861 245.815 394.939 245.815 C 395.369 245.815 395.645 246.532 396.059 247.225 Z M 396.059 247.225" style="fill: rgb(14, 112, 142);"/>
|
||||
<path d="M 403.527 246.924 L 399.174 251.768 C 397.7 250.892 395.578 250.174 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 L 394.341 248.65 L 394.884 248.827 C 395.509 249.031 396.192 248.95 396.753 248.608 L 397.153 248.363 C 397.35 248.454 397.572 248.507 397.823 248.507 C 399.617 248.507 401.955 245.886 401.955 245.886 C 402.494 246.256 403.02 246.601 403.527 246.924 Z M 403.527 246.924" style="fill: rgb(59, 234, 98);"/>
|
||||
<path d="M 413.847 253.602 C 413.847 253.602 411.475 254.468 408.27 254.468 C 407.586 254.468 406.426 254.378 405.025 254.238 L 405.025 254.237 C 405.025 253.495 406.924 251.743 408.366 251.616 C 408.623 252.865 410.097 252.512 411.219 252.128 C 412.341 251.743 413.783 251.167 413.783 251.167 C 414.503 251.888 414.422 253.218 413.847 253.602 Z M 413.847 253.602" style="fill: rgb(8, 124, 250);"/>
|
||||
<path d="M 394.341 248.65 C 394.214 248.978 394.103 249.339 394.016 249.717 C 392.843 249.372 391.985 249.174 391.959 249.168 C 392.108 248.769 392.268 248.379 392.421 248.022 Z M 394.341 248.65" style="fill: rgb(37, 187, 163);"/>
|
||||
<path d="M 408.366 251.616 C 406.924 251.743 405.025 253.495 405.025 254.237 L 405.025 254.238 C 403.784 254.113 402.355 253.948 400.899 253.77 C 400.899 253.051 400.191 252.372 399.174 251.768 L 399.174 251.768 L 403.528 246.924 C 407.331 249.34 410.097 250.43 410.097 250.43 C 410.77 251.102 409.809 251.487 408.366 251.616 Z M 408.366 251.616" style="fill: rgb(248, 245, 231);"/>
|
||||
<polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="406.356 248.463 403.261 252.616 402.183 248.212 400.984 249.899 401.999 254.236 403.927 254.176 407.639 249.062" style="" transform="matrix(0.994522, 0.104529, -0.104529, 0.994522, 28.475005, -40.88594)"/>
|
||||
<g fill="#fb6572" transform="matrix(0.046265, 0, 0, 0.046265, 390.612823, 245.155533)" style="">
|
||||
<path d="m288.839844 72.65625c-2.007813 0-4.011719-.761719-5.542969-2.292969-3.058594-3.0625-3.058594-8.023437 0-11.082031l20.089844-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.0625 8.023438 0 11.082032l-20.089844 20.089843c-1.527344 1.53125-3.535156 2.292969-5.539062 2.292969zm0 0" style="fill: rgb(56, 228, 105);"/>
|
||||
<path d="m314.589844 87.082031c-2.007813 0-4.011719-.765625-5.542969-2.296875-3.0625-3.058594-3.0625-8.019531 0-11.082031l20.089844-20.085937c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.058594 3.0625 3.058594 8.023437 0 11.082031l-20.089844 20.085937c-1.527344 1.53125-3.535156 2.296875-5.539062 2.296875zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
<path d="m340.339844 101.507812c-2.007813 0-4.011719-.765624-5.542969-2.296874-3.058594-3.058594-3.058594-8.023438 0-11.082032l20.089844-20.085937c3.0625-3.0625 8.023437-3.0625 11.082031 0 3.058594 3.058593 3.0625 8.023437 0 11.082031l-20.089844 20.085938c-1.527344 1.53125-3.535156 2.296874-5.539062 2.296874zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
<path d="m366.089844 115.929688c-2.003906 0-4.011719-.761719-5.539063-2.292969-3.0625-3.0625-3.0625-8.023438 0-11.082031l20.085938-20.089844c3.0625-3.058594 8.023437-3.058594 11.082031 0 3.0625 3.0625 3.0625 8.023437 0 11.082031l-20.085938 20.089844c-1.53125 1.53125-3.535156 2.292969-5.542968 2.292969zm0 0" style="fill: rgb(59, 233, 100);"/>
|
||||
</g>
|
||||
<path d="M 401.925 247.748 C 401.761 247.748 401.611 247.629 401.573 247.469 C 401.536 247.312 401.611 247.144 401.753 247.067 C 402 246.933 402.318 247.147 402.286 247.426 C 402.265 247.606 402.107 247.748 401.925 247.748 Z M 401.925 247.748" fill="#1e2628" style=""/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
@@ -8,25 +8,28 @@
|
||||
|
||||
# suppress inspection "UnusedProperty" for whole file
|
||||
|
||||
ideaVersion=2023.3.2
|
||||
#ideaVersion=LATEST-EAP-SNAPSHOT
|
||||
ideaVersion=2024.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-26
|
||||
version=chylex-33
|
||||
javaVersion=17
|
||||
remoteRobotVersion=0.11.21
|
||||
remoteRobotVersion=0.11.22
|
||||
antlrVersion=4.10.1
|
||||
|
||||
kotlin.incremental.useClasspathSnapshot=false
|
||||
|
||||
# Please don't forget to update kotlin version in buildscript section
|
||||
# Also update kotlinxSerializationVersion version
|
||||
kotlinVersion=1.8.21
|
||||
kotlinVersion=1.9.22
|
||||
publishToken=token
|
||||
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=
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
5
gradlew
vendored
5
gradlew
vendored
@@ -130,10 +130,13 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
|
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.23")
|
||||
|
||||
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.10")
|
||||
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.10")
|
||||
implementation("io.ktor:ktor-client-auth:2.3.10")
|
||||
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.9.0.202403050737-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")
|
||||
@@ -93,28 +86,16 @@ tasks.register("addReleaseTag", JavaExec::class) {
|
||||
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
|
||||
}
|
||||
|
||||
tasks.register("resetReleaseBranch", JavaExec::class) {
|
||||
tasks.register("selectBranch", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.ResetReleaseBranchKt")
|
||||
mainClass.set("scripts.release.SelectBranchKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
|
||||
}
|
||||
|
||||
tasks.register("pushChanges", JavaExec::class) {
|
||||
mainClass.set("scripts.PushChangesKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
args = listOf(rootProject.rootDir.toString())
|
||||
}
|
||||
|
||||
tasks.register("pushChangesWithReleaseBranch", JavaExec::class) {
|
||||
mainClass.set("scripts.PushChangesWithReleaseBranchKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
args = listOf(rootProject.rootDir.toString(), releaseType ?: "")
|
||||
}
|
||||
|
||||
tasks.register("selectBranch", JavaExec::class) {
|
||||
tasks.register("eapReleaseActions", JavaExec::class) {
|
||||
group = "release"
|
||||
mainClass.set("scripts.release.SelectBranchKt")
|
||||
mainClass.set("scripts.releaseEap.EapReleaseActionsKt")
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
args = listOf(project.version.toString(), rootProject.rootDir.toString(), releaseType ?: "")
|
||||
}
|
||||
|
@@ -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,13 @@ 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
|
||||
)
|
||||
@@ -41,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()
|
||||
)
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ fun httpClient(): HttpClient {
|
||||
install(Auth) {
|
||||
bearer {
|
||||
loadTokens {
|
||||
val accessToken = System.getenv("YOUTRACK_TOKEN")!!
|
||||
val accessToken = System.getenv("YOUTRACK_TOKEN") ?: error("Missing YOUTRACK_TOKEN")
|
||||
BearerTokens(accessToken, "")
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +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 scripts.release.checkoutBranch
|
||||
import scripts.release.withGit
|
||||
import scripts.release.withRepo
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val rootDir = args[0]
|
||||
println("root dir: $rootDir")
|
||||
|
||||
val currentBranch = withRepo(rootDir) { it.branch }
|
||||
println("Current branch is $currentBranch")
|
||||
|
||||
|
||||
withGit(rootDir) { git ->
|
||||
if (currentBranch != "master") {
|
||||
git.checkoutBranch("master")
|
||||
println("Check out master branch")
|
||||
}
|
||||
|
||||
git.push()
|
||||
.setPushTags()
|
||||
.call()
|
||||
println("Master pushed with tags")
|
||||
|
||||
git.checkoutBranch(currentBranch)
|
||||
println("Checked out $currentBranch branch")
|
||||
}
|
||||
}
|
@@ -1,54 +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 scripts.release.checkoutBranch
|
||||
import scripts.release.withGit
|
||||
import scripts.release.withRepo
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val rootDir = args[0]
|
||||
val releaseType = args[1]
|
||||
println("root dir: $rootDir")
|
||||
println("releaseType: $releaseType")
|
||||
|
||||
val currentBranch = withRepo(rootDir) { it.branch }
|
||||
println("Current branch is $currentBranch")
|
||||
|
||||
|
||||
withGit(rootDir) { git ->
|
||||
if (currentBranch != "master") {
|
||||
git.checkoutBranch("master")
|
||||
println("Check out master branch")
|
||||
}
|
||||
|
||||
git.push()
|
||||
.setPushTags()
|
||||
.call()
|
||||
println("Master pushed with tags")
|
||||
|
||||
if (releaseType != "patch") {
|
||||
git.checkoutBranch("release")
|
||||
println("Checked out release")
|
||||
|
||||
git
|
||||
.push()
|
||||
.setForce(true)
|
||||
.setPushTags()
|
||||
.call()
|
||||
println("Pushed release branch with tags")
|
||||
}
|
||||
else {
|
||||
println("Do not push release branch because type of release is $releaseType")
|
||||
}
|
||||
|
||||
git.checkoutBranch(currentBranch)
|
||||
println("Checked out $currentBranch branch")
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,28 +8,45 @@
|
||||
|
||||
package scripts.release
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.io.path.writeText
|
||||
|
||||
private const val toBeReleased = "## To Be Released"
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("Start updating unreleased section")
|
||||
val (newVersion, rootDir, releaseType) = readArgs(args)
|
||||
|
||||
checkReleaseType(releaseType)
|
||||
|
||||
if (releaseType == "patch") {
|
||||
println("Skip updating the changelog because release type is 'patch'")
|
||||
return
|
||||
}
|
||||
|
||||
val currentDate = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
|
||||
val newItem = "## $newVersion, $currentDate"
|
||||
|
||||
val changelogPath = Path("$rootDir/CHANGES.md")
|
||||
val changelog = changelogPath.readText()
|
||||
val newChangelog = changelog.replace("## To Be Released", newItem)
|
||||
val newChangelog = if (releaseType == "patch") {
|
||||
val decreasedVersion = Semver(newVersion).withIncPatch(-1)
|
||||
val firstEntry = changelog.indexOf("## $decreasedVersion")
|
||||
if (firstEntry != -1) {
|
||||
val newLog = StringBuilder(changelog)
|
||||
newLog.insert(firstEntry, newItem + "\n")
|
||||
newLog.toString()
|
||||
} else {
|
||||
changelog
|
||||
}
|
||||
} else {
|
||||
if (toBeReleased in changelog) {
|
||||
changelog.replace(toBeReleased, newItem)
|
||||
} else {
|
||||
val firstEntry = changelog.indexOf("##")
|
||||
val newLog = StringBuilder(changelog)
|
||||
newLog.insert(firstEntry, newItem + "\n")
|
||||
newLog.toString()
|
||||
}
|
||||
}
|
||||
changelogPath.writeText(newChangelog)
|
||||
}
|
@@ -13,21 +13,20 @@ fun main(args: Array<String>) {
|
||||
|
||||
checkReleaseType(releaseType)
|
||||
|
||||
if (releaseType == "patch") {
|
||||
println("Skip committing changes because release type is 'patch'")
|
||||
return
|
||||
}
|
||||
|
||||
withGit(rootDir) { git ->
|
||||
git
|
||||
.commit()
|
||||
.setAll(true)
|
||||
.setAuthor("IdeaVim Bot", "maintainers@ideavim.dev")
|
||||
.setMessage("Preparation to $newVersion release")
|
||||
.setSign(false)
|
||||
.call()
|
||||
if (git.diff().call().isNotEmpty()) {
|
||||
git
|
||||
.commit()
|
||||
.setAll(true)
|
||||
.setAuthor("IdeaVim Bot", "maintainers@ideavim.dev")
|
||||
.setMessage("Preparation to $newVersion release")
|
||||
.setSign(false)
|
||||
.call()
|
||||
|
||||
val lastGitMessage = git.log().call().first().shortMessage
|
||||
println("Changes committed. Last gitlog message: $lastGitMessage")
|
||||
val lastGitMessage = git.log().call().first().shortMessage
|
||||
println("Changes committed. Last gitlog message: $lastGitMessage")
|
||||
} else {
|
||||
println("No Changes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2003-2023 The IdeaVim authors
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE.txt file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package scripts.release
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val (_, rootDir, releaseType) = readArgs(args)
|
||||
|
||||
checkReleaseType(releaseType)
|
||||
|
||||
checkBranch(rootDir, releaseType)
|
||||
|
||||
if (releaseType == "patch") {
|
||||
println("Skip release branch reset because release type is 'patch'")
|
||||
return
|
||||
}
|
||||
|
||||
withGit(rootDir) { git ->
|
||||
val currentCommit = git.log().setMaxCount(1).call().first()
|
||||
println("Current commit id: ${currentCommit.id.name}")
|
||||
|
||||
git.checkoutBranch("release")
|
||||
println("Checked out release branch")
|
||||
|
||||
git.reset()
|
||||
.setRef(currentCommit.id.name)
|
||||
.call()
|
||||
println("release branch reset")
|
||||
|
||||
git.checkoutBranch("master")
|
||||
println("Checked out master branch")
|
||||
}
|
||||
}
|
@@ -15,8 +15,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
withGit(rootDir) { git ->
|
||||
val branchName = when (releaseType) {
|
||||
"major", "minor" -> "master"
|
||||
"patch" -> "release"
|
||||
"major", "minor", "patch" -> "release"
|
||||
else -> error("Unsupported release type: $releaseType")
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 scripts.releaseEap
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import scripts.addComment
|
||||
import scripts.getYoutrackTicketsByQuery
|
||||
import scripts.release.readArgs
|
||||
import scripts.releasedInEapTagId
|
||||
import scripts.setTag
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runBlocking {
|
||||
val (newVersion, _, _) = readArgs(args)
|
||||
|
||||
// Search for Ready to release, but without "IdeaVim Released In EAP" tag
|
||||
val ticketsToUpdate =
|
||||
getYoutrackTicketsByQuery("%23%7BReady%20To%20Release%7D%20tag:%20-%7BIdeaVim%20Released%20In%20EAP%7D%20")
|
||||
println("Have to update the following tickets: $ticketsToUpdate")
|
||||
|
||||
ticketsToUpdate.forEach { ticketId ->
|
||||
setTag(ticketId, releasedInEapTagId)
|
||||
addComment(
|
||||
ticketId, """
|
||||
The fix is available in the IdeaVim $newVersion. See https://jb.gg/ideavim-eap for the instructions on how to get EAP builds as updates within the IDE. You can also wait till the next stable release with this fix, you’ll get it automatically.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
}
|
@@ -11,6 +11,8 @@ package scripts
|
||||
import io.ktor.client.call.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.http.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.addJsonObject
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
@@ -21,6 +23,10 @@ import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
|
||||
|
||||
// YouTrack tag "IdeaVim Released In EAP"
|
||||
const val releasedInEapTagId = "68-385032"
|
||||
|
||||
suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
|
||||
val client = httpClient()
|
||||
|
||||
@@ -59,3 +65,59 @@ suspend fun setYoutrackStatus(tickets: Collection<String>, status: String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getYoutrackTicketsByQuery(query: String): Set<String> {
|
||||
val client = httpClient()
|
||||
|
||||
return runBlocking {
|
||||
val response = client.get("https://youtrack.jetbrains.com/api/issues/?fields=idReadable&query=project:VIM+$query")
|
||||
response.body<JsonArray>().mapTo(HashSet()) { it.jsonObject.getValue("idReadable").jsonPrimitive.content }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 68-385032
|
||||
* [issueHumanId] is like VIM-123
|
||||
* [tagId] is like "145-23"
|
||||
*/
|
||||
suspend fun setTag(issueHumanId: String, tagId: String) {
|
||||
val client = httpClient()
|
||||
|
||||
println("Try to add tag $tagId to $issueHumanId")
|
||||
val response =
|
||||
// I've updated default url in client, so this may be broken now
|
||||
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/tags?fields=customFields(id,name,value(id,name))") {
|
||||
contentType(ContentType.Application.Json)
|
||||
accept(ContentType.Application.Json)
|
||||
val request = buildJsonObject {
|
||||
put("id", tagId)
|
||||
}
|
||||
setBody(request)
|
||||
}
|
||||
println(response)
|
||||
println(response.body<String>())
|
||||
if (!response.status.isSuccess()) {
|
||||
error("Request failed. $issueHumanId, ${response.body<String>()}")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun addComment(issueHumanId: String, text: String) {
|
||||
val client = httpClient()
|
||||
|
||||
println("Try to add comment to $issueHumanId")
|
||||
val response =
|
||||
// I've updated default url in client, so this may be broken now
|
||||
client.post("https://youtrack.jetbrains.com/api/issues/$issueHumanId/comments?fields=customFields(id,name,value(id,name))") {
|
||||
contentType(ContentType.Application.Json)
|
||||
accept(ContentType.Application.Json)
|
||||
val request = buildJsonObject {
|
||||
put("text", text)
|
||||
}
|
||||
setBody(request)
|
||||
}
|
||||
println(response)
|
||||
println(response.body<String>())
|
||||
if (!response.status.isSuccess()) {
|
||||
error("Request failed. $issueHumanId, ${response.body<String>()}")
|
||||
}
|
||||
}
|
||||
|
@@ -12,4 +12,9 @@ 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)
|
||||
|
@@ -7,56 +7,27 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.maddyhome.idea.vim.action.EngineCommandProvider
|
||||
import com.maddyhome.idea.vim.action.IntellijCommandProvider
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.handler.ActionBeanClass
|
||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
||||
import com.maddyhome.idea.vim.key.MappingOwner
|
||||
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator
|
||||
import com.maddyhome.idea.vim.newapi.globalIjOptions
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
public object RegisterActions {
|
||||
@Deprecated("Please use @CommandOrMotion annotation instead")
|
||||
internal val VIM_ACTIONS_EP: ExtensionPointName<ActionBeanClass> = ExtensionPointName.create("IdeaVIM.vimAction")
|
||||
|
||||
/**
|
||||
* Register all the key/action mappings for the plugin.
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun registerActions() {
|
||||
registerVimCommandActions()
|
||||
if (!injector.globalIjOptions().commandOrMotionAnnotation) {
|
||||
registerEmptyShortcuts()
|
||||
registerEpListener()
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Moving to annotations approach instead of xml")
|
||||
private fun registerEpListener() {
|
||||
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
|
||||
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
|
||||
VIM_ACTIONS_EP.addChangeListener({
|
||||
unregisterActions()
|
||||
registerActions()
|
||||
}, VimPlugin.getInstance())
|
||||
registerShortcutsWithoutActions()
|
||||
}
|
||||
|
||||
public fun findAction(id: String): EditorActionHandlerBase? {
|
||||
if (injector.globalIjOptions().commandOrMotionAnnotation) {
|
||||
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
return commandBean.instance
|
||||
} else {
|
||||
return VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream()
|
||||
.filter { vimActionBean: ActionBeanClass -> vimActionBean.actionId == id }
|
||||
.findFirst().map { obj: ActionBeanClass -> obj.instance }
|
||||
.orElse(null)
|
||||
}
|
||||
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
|
||||
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
|
||||
return commandBean.instance
|
||||
}
|
||||
|
||||
public fun findActionOrDie(id: String): EditorActionHandlerBase {
|
||||
@@ -71,30 +42,15 @@ public object RegisterActions {
|
||||
|
||||
private fun registerVimCommandActions() {
|
||||
val parser = VimPlugin.getKey()
|
||||
if (injector.globalIjOptions().commandOrMotionAnnotation) {
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
} else {
|
||||
VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream().map { bean: ActionBeanClass? ->
|
||||
IjVimActionsInitiator(
|
||||
bean!!
|
||||
)
|
||||
}
|
||||
.forEach { actionHolder: IjVimActionsInitiator? ->
|
||||
parser.registerCommandAction(
|
||||
actionHolder!!
|
||||
)
|
||||
}
|
||||
}
|
||||
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
|
||||
}
|
||||
|
||||
// todo do we really need this?
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@@ -39,11 +39,9 @@ import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimInjector;
|
||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.vimscript.services.OptionService;
|
||||
import com.maddyhome.idea.vim.vimscript.services.VariableService;
|
||||
import com.maddyhome.idea.vim.yank.YankGroupBase;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -117,12 +115,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return ApplicationManager.getApplication().getService(CommandGroup.class);
|
||||
}
|
||||
|
||||
@Deprecated // "Please use `injector.markService` instead"
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
|
||||
public static @NotNull MarkGroup getMark() {
|
||||
return ApplicationManager.getApplication().getService(MarkGroup.class);
|
||||
}
|
||||
|
||||
public static @NotNull RegisterGroup getRegister() {
|
||||
return ((RegisterGroup)VimInjectorKt.getInjector().getRegisterGroup());
|
||||
}
|
||||
@@ -195,13 +187,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return VimInjectorKt.getInjector().getOptionGroup();
|
||||
}
|
||||
|
||||
/** Deprecated: Use getOptionGroup */
|
||||
@Deprecated
|
||||
// Used by which-key 0.8.0, IdeaVimExtension 1.6.5 + 1.6.8
|
||||
public static @NotNull OptionService getOptionService() {
|
||||
return VimInjectorKt.getInjector().getOptionService();
|
||||
}
|
||||
|
||||
private static @NotNull NotificationService getNotifications() {
|
||||
return getNotifications(null);
|
||||
}
|
||||
@@ -226,22 +211,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();
|
||||
}
|
||||
|
||||
@@ -368,6 +353,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
|
||||
if (onOffDisposable != null) {
|
||||
Disposer.dispose(onOffDisposable);
|
||||
onOffDisposable = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.maddyhome.idea.vim.group.EditorHolderService
|
||||
|
||||
@Service
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class VimProjectService(val project: Project) : Disposable {
|
||||
override fun dispose() {
|
||||
// Not sure if this is a best solution
|
||||
|
@@ -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")
|
||||
|
@@ -28,6 +28,7 @@ import com.maddyhome.idea.vim.api.globalOptions
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.group.IjOptionConstants
|
||||
import com.maddyhome.idea.vim.group.IjOptions
|
||||
import com.maddyhome.idea.vim.handler.enableOctopus
|
||||
import com.maddyhome.idea.vim.handler.isOctopusEnabled
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.HandlerInjector
|
||||
@@ -43,7 +44,6 @@ 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.vimscript.model.datatypes.VimString
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.KeyEvent
|
||||
@@ -78,11 +78,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")
|
||||
@@ -116,11 +113,13 @@ public class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible
|
||||
if (VimPlugin.isNotEnabled()) return ActionEnableStatus.no("IdeaVim is disabled", LogLevel.DEBUG)
|
||||
val editor = getEditor(e)
|
||||
if (editor != null && keyStroke != null) {
|
||||
if (isOctopusEnabled(keyStroke, editor)) {
|
||||
return ActionEnableStatus.no(
|
||||
"Processing VimShortcutKeyAction for the key that is used in the octopus handler",
|
||||
LogLevel.ERROR
|
||||
)
|
||||
if (enableOctopus) {
|
||||
if (isOctopusEnabled(keyStroke, editor)) {
|
||||
return ActionEnableStatus.no(
|
||||
"Processing VimShortcutKeyAction for the key that is used in the octopus handler",
|
||||
LogLevel.ERROR
|
||||
)
|
||||
}
|
||||
}
|
||||
if (editor.isIdeaVimDisabledHere) {
|
||||
return ActionEnableStatus.no("IdeaVim is disabled in this place", LogLevel.INFO)
|
||||
@@ -377,6 +376,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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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(
|
||||
|
@@ -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(
|
||||
|
@@ -8,10 +8,9 @@
|
||||
|
||||
package com.maddyhome.idea.vim.action.editor
|
||||
|
||||
import com.intellij.openapi.actionSystem.IdeActions
|
||||
import com.intellij.vim.annotations.CommandOrMotion
|
||||
import com.intellij.vim.annotations.Mode
|
||||
import com.intellij.openapi.actionSystem.IdeActions
|
||||
import com.maddyhome.idea.vim.action.ComplicatedKeysAction
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
@@ -21,54 +20,32 @@ import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.IdeActionHandler
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
@CommandOrMotion(keys = ["<C-H>", "<BS>"], modes = [Mode.INSERT])
|
||||
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)),
|
||||
)
|
||||
internal class VimEditorBackSpace : IdeActionHandler(IdeActions.ACTION_EDITOR_BACKSPACE) {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Del>"], modes = [Mode.INSERT])
|
||||
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)),
|
||||
)
|
||||
internal class VimEditorDelete : IdeActionHandler(IdeActions.ACTION_EDITOR_DELETE) {
|
||||
override val type: Command.Type = Command.Type.DELETE
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Down>", "<kDown>"], modes = [Mode.INSERT])
|
||||
internal class VimEditorDown : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_DOWN), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, 0)),
|
||||
)
|
||||
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)
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Tab>", "<C-I>"], modes = [Mode.INSERT])
|
||||
internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)),
|
||||
)
|
||||
internal class VimEditorTab : IdeActionHandler(IdeActions.ACTION_EDITOR_TAB) {
|
||||
override val type: Command.Type = Command.Type.INSERT
|
||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||
}
|
||||
|
||||
@CommandOrMotion(keys = ["<Up>", "<kUp>"], modes = [Mode.INSERT])
|
||||
internal class VimEditorUp : IdeActionHandler(IdeActions.ACTION_EDITOR_MOVE_CARET_UP), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0)),
|
||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, 0)),
|
||||
)
|
||||
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)
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ 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.action.ComplicatedKeysAction
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.command.Command
|
||||
@@ -18,7 +17,6 @@ import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
* Called by KeyHandler to process the contents of the ex entry panel
|
||||
@@ -26,10 +24,7 @@ import javax.swing.KeyStroke
|
||||
* 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(), ComplicatedKeysAction {
|
||||
override val keyStrokesSet: Set<List<KeyStroke>> =
|
||||
parseKeysSet("<CR>", "<C-M>", 0x0a.toChar().toString(), 0x0d.toChar().toString())
|
||||
|
||||
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)
|
||||
|
@@ -21,19 +21,19 @@ import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||
public class CommandState(private val machine: VimStateMachine) {
|
||||
|
||||
public val isOperatorPending: Boolean
|
||||
get() = machine.isOperatorPending
|
||||
get() = machine.isOperatorPending(machine.mode)
|
||||
|
||||
public val mode: CommandState.Mode
|
||||
public val mode: 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
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.CMD_LINE -> Mode.CMD_LINE
|
||||
com.maddyhome.idea.vim.state.mode.Mode.INSERT -> Mode.INSERT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.NORMAL -> Mode.COMMAND
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING -> Mode.OP_PENDING
|
||||
com.maddyhome.idea.vim.state.mode.Mode.REPLACE -> Mode.REPLACE
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.SELECT -> Mode.SELECT
|
||||
is com.maddyhome.idea.vim.state.mode.Mode.VISUAL -> Mode.VISUAL
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
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
|
||||
@@ -14,21 +15,34 @@ 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.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.CommandLineHelper
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -135,8 +143,9 @@ public object VimExtensionFacade {
|
||||
*/
|
||||
@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) }
|
||||
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()'. */
|
||||
@@ -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,8 +182,8 @@ 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) ?: ""
|
||||
public fun inputString(editor: Editor, context: DataContext, prompt: String, finishOn: Char?): String {
|
||||
return service<CommandLineHelper>().inputString(editor.vim, context.vim, prompt, finishOn) ?: ""
|
||||
}
|
||||
|
||||
/** Get the current contents of the given register similar to 'getreg()'. */
|
||||
@@ -207,4 +216,65 @@ public object VimExtensionFacade {
|
||||
public fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
VimPlugin.getRegister().setKeys(register, keys?.filterNotNull() ?: emptyList(), type)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
public 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)
|
||||
}
|
||||
}
|
||||
|
||||
public 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun interface ScriptFunction {
|
||||
public fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
|
||||
}
|
@@ -53,6 +53,11 @@ internal object VimExtensionRegistrar : VimExtensionRegistrator {
|
||||
@Synchronized
|
||||
private fun registerExtension(extensionBean: ExtensionBeanClass) {
|
||||
val name = extensionBean.name ?: extensionBean.instance.name
|
||||
if (name == "sneak" && extensionBean.name == null) {
|
||||
// Filter out the old ideavim-sneak extension that used to be a separate plugin
|
||||
// https://github.com/Mishkun/ideavim-sneak
|
||||
return
|
||||
}
|
||||
if (name in registeredExtensions) return
|
||||
|
||||
registeredExtensions.add(name)
|
||||
@@ -62,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()
|
||||
}
|
||||
|
@@ -251,7 +251,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
||||
|
||||
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
editor.nativeCarets().forEach((VimCaret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0);
|
||||
if (range != null) {
|
||||
|
@@ -22,26 +22,26 @@ 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.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 +49,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 +69,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 +114,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 +151,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,19 +169,13 @@ 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 {
|
||||
@@ -239,7 +249,7 @@ internal class CommentaryExtension : VimExtension {
|
||||
*/
|
||||
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)
|
||||
Util.doCommentary(editor, context, ranges.getTextRange(editor, -1), 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
|
||||
}
|
||||
}
|
||||
|
@@ -99,7 +99,7 @@ internal class Matchit : VimExtension {
|
||||
|
||||
// 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 = commandState.isOperatorPending(editor.mode)
|
||||
|
||||
if (isInOpPending) {
|
||||
val matchitAction = MatchitAction()
|
||||
|
@@ -246,7 +246,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
|
||||
// Note that ignoreCase is not overridden by the `\C` in the pattern
|
||||
val pattern = makePattern(text, whole)
|
||||
val matches = SearchHelper.findAll(editor, pattern, 0, -1, false)
|
||||
val matches = injector.searchHelper.findAll(IjVimEditor(editor), pattern, 0, -1, false)
|
||||
for (match in matches) {
|
||||
if (match.contains(primaryCaret.offset)) {
|
||||
primaryCaret.vim.moveToOffset(match.startOffset)
|
||||
@@ -322,7 +322,7 @@ internal class VimMultipleCursorsExtension : VimExtension {
|
||||
searchOptions.add(SearchOptions.WRAP)
|
||||
}
|
||||
|
||||
return SearchHelper.findPattern(editor, makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1
|
||||
return injector.searchHelper.findPattern(IjVimEditor(editor), makePattern(text, whole), startOffset, 1, searchOptions)?.startOffset ?: -1
|
||||
}
|
||||
|
||||
private fun makePattern(text: String, whole: Boolean): String {
|
||||
|
@@ -8,25 +8,21 @@
|
||||
|
||||
package com.maddyhome.idea.vim.extension.nerdtree
|
||||
|
||||
import com.intellij.ide.projectView.ProjectView
|
||||
import com.intellij.ide.projectView.impl.AbstractProjectViewPane
|
||||
import com.intellij.ide.projectView.impl.ProjectViewImpl
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.PlatformCoreDataKeys
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.openapi.wm.ToolWindow
|
||||
import com.intellij.openapi.ui.getUserData
|
||||
import com.intellij.openapi.ui.putUserData
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.wm.ToolWindowId
|
||||
import com.intellij.openapi.wm.ex.ToolWindowManagerEx
|
||||
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
|
||||
import com.intellij.ui.KeyStrokeAdapter
|
||||
import com.intellij.ui.TreeExpandCollapse
|
||||
import com.intellij.ui.speedSearch.SpeedSearchSupply
|
||||
@@ -53,6 +49,8 @@ import com.maddyhome.idea.vim.newapi.ij
|
||||
import com.maddyhome.idea.vim.newapi.vim
|
||||
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimString
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JTree
|
||||
import javax.swing.KeyStroke
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
@@ -130,15 +128,14 @@ internal class NerdTree : VimExtension {
|
||||
addCommand("NERDTreeFind", IjCommandHandler("SelectInProjectView"))
|
||||
addCommand("NERDTreeRefreshRoot", IjCommandHandler("Synchronize"))
|
||||
|
||||
synchronized(monitor) {
|
||||
commandsRegistered = true
|
||||
ProjectManager.getInstance().openProjects.forEach { project -> installDispatcher(project) }
|
||||
synchronized(Util.monitor) {
|
||||
Util.commandsRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
class IjCommandHandler(private val actionId: String) : CommandAliasHandler {
|
||||
override fun execute(command: String, ranges: Ranges, editor: VimEditor, context: ExecutionContext) {
|
||||
callAction(editor, actionId, context)
|
||||
Util.callAction(editor, actionId, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +146,7 @@ internal class NerdTree : VimExtension {
|
||||
if (toolWindow.isVisible) {
|
||||
toolWindow.hide()
|
||||
} else {
|
||||
callAction(editor, "ActivateProjectToolWindow", context)
|
||||
Util.callAction(editor, "ActivateProjectToolWindow", context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,39 +161,8 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
|
||||
class ProjectViewListener(private val project: Project) : ToolWindowManagerListener {
|
||||
override fun toolWindowShown(toolWindow: ToolWindow) {
|
||||
if (ToolWindowId.PROJECT_VIEW != toolWindow.id) return
|
||||
|
||||
val dispatcher = NerdDispatcher.getInstance(project)
|
||||
if (dispatcher.speedSearchListenerInstalled) return
|
||||
|
||||
// I specify nullability explicitly as we've got a lot of exceptions saying this property is null
|
||||
val currentProjectViewPane: AbstractProjectViewPane? = ProjectView.getInstance(project).currentProjectViewPane
|
||||
val tree = currentProjectViewPane?.tree ?: return
|
||||
val supply = SpeedSearchSupply.getSupply(tree, true) ?: return
|
||||
|
||||
// NB: Here might be some issues with concurrency, but it's not really bad, I think
|
||||
dispatcher.speedSearchListenerInstalled = true
|
||||
supply.addChangeListener {
|
||||
dispatcher.waitForSearch = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
installDispatcher(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NerdDispatcher : DumbAwareAction() {
|
||||
internal var waitForSearch = false
|
||||
internal var speedSearchListenerInstalled = false
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
var keyStroke = getKeyStroke(e) ?: return
|
||||
@@ -214,7 +180,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) }
|
||||
}
|
||||
}
|
||||
@@ -244,10 +210,6 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): NerdDispatcher {
|
||||
return project.getService(NerdDispatcher::class.java)
|
||||
}
|
||||
|
||||
private const val ESCAPE_KEY_CODE = 27
|
||||
}
|
||||
|
||||
@@ -283,19 +245,14 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapActivateNode",
|
||||
"o",
|
||||
NerdAction.Code { project, dataContext, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, dataContext, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
|
||||
val array = CommonDataKeys.NAVIGATABLE_ARRAY.getData(dataContext)?.filter { it.canNavigateToSource() }
|
||||
if (array.isNullOrEmpty()) {
|
||||
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
||||
if (tree.isExpanded(row)) {
|
||||
tree.collapseRow(row)
|
||||
} else {
|
||||
tree.expandRow(row)
|
||||
}
|
||||
val row = tree.selectionRows?.getOrNull(0) ?: return@Code
|
||||
if (tree.isExpanded(row)) {
|
||||
tree.collapseRow(row)
|
||||
} else {
|
||||
array.forEach { it.navigate(true) }
|
||||
tree.expandRow(row)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -356,7 +313,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,14 +325,14 @@ 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(
|
||||
"NERDTreeMapOpenRecursively",
|
||||
"O",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
TreeExpandCollapse.expandAll(tree)
|
||||
tree.selectionPath?.let {
|
||||
TreeUtil.scrollToVisible(tree, it, false)
|
||||
@@ -385,8 +342,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapCloseChildren",
|
||||
"X",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
TreeExpandCollapse.collapse(tree)
|
||||
tree.selectionPath?.let {
|
||||
TreeUtil.scrollToVisible(tree, it, false)
|
||||
@@ -396,8 +353,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapCloseDir",
|
||||
"x",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
if (tree.isExpanded(currentPath)) {
|
||||
tree.collapsePath(currentPath)
|
||||
@@ -415,8 +372,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpParent",
|
||||
"p",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
val parentPath = currentPath.parentPath ?: return@Code
|
||||
if (parentPath.parentPath != null) {
|
||||
@@ -429,8 +386,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpFirstChild",
|
||||
"K",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
val parent = currentPath.parentPath ?: return@Code
|
||||
val row = tree.getRowForPath(parent)
|
||||
@@ -442,8 +399,8 @@ internal class NerdTree : VimExtension {
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpLastChild",
|
||||
"J",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val tree = ProjectView.getInstance(project).currentProjectViewPane.tree
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
val currentPath = tree.selectionPath ?: return@Code
|
||||
|
||||
val currentPathCount = currentPath.pathCount
|
||||
@@ -464,12 +421,12 @@ internal class NerdTree : VimExtension {
|
||||
)
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpNextSibling",
|
||||
"<C-J>",
|
||||
"<A-J>",
|
||||
NerdAction.ToIj("Tree-selectNextSibling"),
|
||||
)
|
||||
registerCommand(
|
||||
"NERDTreeMapJumpPrevSibling",
|
||||
"<C-K>",
|
||||
"<A-K>",
|
||||
NerdAction.ToIj("Tree-selectPreviousSibling"),
|
||||
)
|
||||
registerCommand(
|
||||
@@ -488,30 +445,31 @@ internal class NerdTree : VimExtension {
|
||||
|
||||
registerCommand(
|
||||
"/",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
NerdDispatcher.getInstance(project).waitForSearch = true
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
tree.getUserData(KEY)?.waitForSearch = true
|
||||
},
|
||||
)
|
||||
|
||||
registerCommand(
|
||||
"<ESC>",
|
||||
NerdAction.Code { project, _, _ ->
|
||||
val instance = NerdDispatcher.getInstance(project)
|
||||
if (instance.waitForSearch) {
|
||||
instance.waitForSearch = false
|
||||
}
|
||||
NerdAction.Code { _, _, e ->
|
||||
val tree = getTree(e) ?: return@Code
|
||||
tree.getUserData(KEY)?.waitForSearch = false
|
||||
},
|
||||
)
|
||||
|
||||
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))
|
||||
@@ -526,45 +484,60 @@ internal class NerdTree : VimExtension {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCommand(alias: String, handler: CommandAliasHandler) {
|
||||
VimPlugin.getCommand().setAlias(alias, CommandAlias.Call(0, -1, alias, handler))
|
||||
}
|
||||
companion object {
|
||||
const val pluginName = "NERDTree"
|
||||
private val LOG = logger<NerdTree>()
|
||||
private val KEY = Key.create<NerdDispatcher>("IdeaVim-NerdTree-Dispatcher")
|
||||
|
||||
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)
|
||||
}
|
||||
fun installDispatcher(component: JComponent) {
|
||||
if (component.getUserData(KEY) != null) return
|
||||
|
||||
private fun registerCommand(default: String, action: NerdAction) {
|
||||
actionsRoot.addLeafs(default, action)
|
||||
}
|
||||
val dispatcher = NerdDispatcher()
|
||||
component.putUserData(KEY, dispatcher)
|
||||
|
||||
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,
|
||||
)
|
||||
dispatcher.registerCustomShortcutSet(KeyGroup.toShortcutSet(shortcuts), component)
|
||||
|
||||
SpeedSearchSupply.getSupply(component, true)?.addChangeListener {
|
||||
dispatcher.waitForSearch = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 getTree(e: AnActionEvent): JTree? {
|
||||
return e.dataContext.getData(PlatformCoreDataKeys.CONTEXT_COMPONENT) as? JTree
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
package com.maddyhome.idea.vim.extension.nerdtree
|
||||
|
||||
import com.intellij.ide.ApplicationInitializedListener
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.util.ui.StartupUiUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.awt.AWTEvent
|
||||
import java.awt.event.FocusEvent
|
||||
import javax.swing.JTree
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
internal class NerdTreeApplicationListener : ApplicationInitializedListener {
|
||||
override suspend fun execute(asyncScope: CoroutineScope) {
|
||||
StartupUiUtil.addAwtListener(::handleEvent, AWTEvent.FOCUS_EVENT_MASK, ApplicationManager.getApplication().getService(NerdTreeDisposableService::class.java))
|
||||
}
|
||||
|
||||
private fun handleEvent(event: AWTEvent) {
|
||||
if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED) {
|
||||
val source = event.source
|
||||
if (source is JTree) {
|
||||
NerdTree.installDispatcher(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.maddyhome.idea.vim.extension.nerdtree
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
|
||||
@Service
|
||||
internal class NerdTreeDisposableService : Disposable {
|
||||
override fun dispose() {}
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
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.injector
|
||||
@@ -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 {
|
||||
@@ -49,4 +52,17 @@ internal class ParagraphMotion : VimExtension {
|
||||
?.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,30 +8,26 @@
|
||||
|
||||
package com.maddyhome.idea.vim.extension.replacewithregister
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
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
|
||||
@@ -39,6 +35,10 @@ 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,49 @@ 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 = 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,
|
||||
)
|
||||
val vimEditor = editor.vim
|
||||
ClipboardOptionHelper.IdeaputDisabler().use {
|
||||
VimPlugin.getPut().putText(
|
||||
vimEditor,
|
||||
context.vim,
|
||||
putData,
|
||||
operatorArguments = OperatorArguments(
|
||||
editor.vimStateMachine?.isOperatorPending(vimEditor.mode) ?: false,
|
||||
0,
|
||||
editor.vim.mode,
|
||||
),
|
||||
saveToRegister = false
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* 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.extension.sneak
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.ScrollType
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.markup.EffectType
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
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.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.VimProjectService
|
||||
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.api.options
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
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.putKeyMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||
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
|
||||
|
||||
|
||||
private const val DEFAULT_HIGHLIGHT_DURATION_SNEAK = 300
|
||||
|
||||
// By [Mikhail Levchenko](https://github.com/Mishkun)
|
||||
// Original repository with the plugin: https://github.com/Mishkun/ideavim-sneak
|
||||
internal class IdeaVimSneakExtension : VimExtension {
|
||||
override fun getName(): String = "sneak"
|
||||
|
||||
override fun init() {
|
||||
val highlightHandler = HighlightHandler()
|
||||
mapToFunctionAndProvideKeys("s", SneakHandler(highlightHandler, Direction.FORWARD), MappingMode.NXO)
|
||||
|
||||
// vim-sneak uses `Z` for visual mode because `S` conflict with vim-sneak plugin VIM-3330
|
||||
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"), 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), MappingMode.NXO)
|
||||
mapToFunctionAndProvideKeys(",", SneakRepeatHandler(highlightHandler, RepeatDirection.REVERSE), MappingMode.NXO)
|
||||
}
|
||||
|
||||
private class SneakHandler(
|
||||
private val highlightHandler: HighlightHandler,
|
||||
private val direction: Direction,
|
||||
) : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val charone = getChar(editor) ?: return
|
||||
val chartwo = getChar(editor) ?: return
|
||||
val range = Util.jumpTo(editor, charone, chartwo, direction)
|
||||
range?.let { highlightHandler.highlightSneakRange(editor.ij, range) }
|
||||
Util.lastSymbols = "${charone}${chartwo}"
|
||||
Util.lastSDirection = direction
|
||||
}
|
||||
|
||||
private fun getChar(editor: VimEditor): Char? {
|
||||
val key = VimExtensionFacade.inputKeyStroke(editor.ij)
|
||||
return when {
|
||||
key.keyChar == KeyEvent.CHAR_UNDEFINED || key.keyCode == KeyEvent.VK_ESCAPE -> null
|
||||
else -> key.keyChar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class acts as proxy for normal find commands because we need to update [Util.lastSDirection]
|
||||
*/
|
||||
private class SneakMemoryHandler(private val char: String) : VimExtensionHandler {
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
Util.lastSDirection = null
|
||||
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(char), editor)
|
||||
}
|
||||
}
|
||||
|
||||
private class SneakRepeatHandler(
|
||||
private val highlightHandler: HighlightHandler,
|
||||
private val direction: RepeatDirection,
|
||||
) : ExtensionHandler {
|
||||
override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val lastSDirection = Util.lastSDirection
|
||||
if (lastSDirection != null) {
|
||||
val (charone, chartwo) = Util.lastSymbols.toList()
|
||||
val jumpRange = Util.jumpTo(editor, charone, chartwo, direction.map(lastSDirection))
|
||||
jumpRange?.let { highlightHandler.highlightSneakRange(editor.ij, jumpRange) }
|
||||
} else {
|
||||
VimExtensionFacade.executeNormalWithoutMapping(injector.parser.parseKeys(direction.symb), editor.ij)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object Util {
|
||||
var lastSDirection: Direction? = null
|
||||
var lastSymbols: String = ""
|
||||
fun jumpTo(editor: VimEditor, charone: Char, chartwo: Char, sneakDirection: Direction): TextRange? {
|
||||
val caret = editor.primaryCaret()
|
||||
val position = caret.offset
|
||||
val chars = editor.text()
|
||||
val foundPosition = sneakDirection.findBiChar(editor, chars, position, charone, chartwo)
|
||||
if (foundPosition != null) {
|
||||
editor.primaryCaret().moveToOffset(foundPosition)
|
||||
}
|
||||
editor.ij.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE)
|
||||
return foundPosition?.let { TextRange(foundPosition, foundPosition + 2) }
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Direction(val offset: Int) {
|
||||
FORWARD(1) {
|
||||
override fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char
|
||||
): Int? {
|
||||
for (i in (position + offset) until charSequence.length - 1) {
|
||||
if (matches(editor, charSequence, i, charone, chartwo)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
},
|
||||
BACKWARD(-1) {
|
||||
override fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char
|
||||
): Int? {
|
||||
for (i in (position + offset) downTo 0) {
|
||||
if (matches(editor, charSequence, i, charone, chartwo)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
abstract fun findBiChar(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
position: Int,
|
||||
charone: Char,
|
||||
chartwo: Char,
|
||||
): Int?
|
||||
|
||||
fun matches(
|
||||
editor: VimEditor,
|
||||
charSequence: CharSequence,
|
||||
charPosition: Int,
|
||||
charOne: Char,
|
||||
charTwo: Char,
|
||||
): Boolean {
|
||||
var match = charSequence[charPosition].equals(charOne, ignoreCase = injector.options(editor).ignorecase) &&
|
||||
charSequence[charPosition + 1].equals(charTwo, ignoreCase = injector.options(editor).ignorecase)
|
||||
|
||||
if (injector.options(editor).ignorecase && injector.options(editor).smartcase) {
|
||||
if (charOne.isUpperCase() || charTwo.isUpperCase()) {
|
||||
match = charSequence[charPosition].equals(charOne, ignoreCase = false) &&
|
||||
charSequence[charPosition + 1].equals(charTwo, ignoreCase = false)
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
}
|
||||
|
||||
private enum class RepeatDirection(val symb: String) {
|
||||
IDENTICAL(";") {
|
||||
override fun map(direction: Direction): Direction = direction
|
||||
},
|
||||
REVERSE(",") {
|
||||
override fun map(direction: Direction): Direction = when (direction) {
|
||||
Direction.FORWARD -> Direction.BACKWARD
|
||||
Direction.BACKWARD -> Direction.FORWARD
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun map(direction: Direction): Direction
|
||||
}
|
||||
|
||||
private class HighlightHandler {
|
||||
private var editor: Editor? = null
|
||||
private val sneakHighlighters: MutableSet<RangeHighlighter> = mutableSetOf()
|
||||
|
||||
fun highlightSneakRange(editor: Editor, range: TextRange) {
|
||||
clearAllSneakHighlighters()
|
||||
|
||||
this.editor = editor
|
||||
val project = editor.project
|
||||
if (project != null) {
|
||||
Disposer.register(VimProjectService.getInstance(project)) {
|
||||
this.editor = null
|
||||
sneakHighlighters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
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 clearAllSneakHighlighters() {
|
||||
sneakHighlighters.forEach { highlighter ->
|
||||
editor?.markupModel?.removeHighlighter(highlighter) ?: StrictMode.fail("Highlighters without an editor")
|
||||
}
|
||||
|
||||
sneakHighlighters.clear()
|
||||
}
|
||||
|
||||
private fun highlightSingleRange(editor: Editor, range: ClosedRange<Int>) {
|
||||
val highlighter = editor.markupModel.addRangeHighlighter(
|
||||
range.start,
|
||||
range.endInclusive,
|
||||
HighlighterLayer.SELECTION,
|
||||
getHighlightTextAttributes(),
|
||||
HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
|
||||
sneakHighlighters.add(highlighter)
|
||||
|
||||
setClearHighlightRangeTimer(highlighter)
|
||||
}
|
||||
|
||||
private fun setClearHighlightRangeTimer(highlighter: RangeHighlighter) {
|
||||
val timer = Timer(DEFAULT_HIGHLIGHT_DURATION_SNEAK) {
|
||||
if (editor?.isDisposed != true) {
|
||||
editor?.markupModel?.removeHighlighter(highlighter)
|
||||
}
|
||||
}
|
||||
timer.isRepeats = false
|
||||
timer.start()
|
||||
}
|
||||
|
||||
private fun getHighlightTextAttributes() = TextAttributes(
|
||||
null,
|
||||
EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES.defaultAttributes.backgroundColor,
|
||||
editor?.colorsScheme?.getColor(EditorColors.CARET_COLOR),
|
||||
EffectType.SEARCH_MATCH,
|
||||
Font.PLAIN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map some <Plug>(keys) command to given handler
|
||||
* and create mapping to <Plug>(prefix)[keys]
|
||||
*/
|
||||
private fun VimExtension.mapToFunctionAndProvideKeys(
|
||||
keys: String, handler: ExtensionHandler, mappingModes: EnumSet<MappingMode>
|
||||
) {
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(command(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
VimExtensionFacade.putExtensionHandlerMapping(
|
||||
mappingModes,
|
||||
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
|
||||
owner,
|
||||
handler,
|
||||
false
|
||||
)
|
||||
|
||||
// This is a combination to meet the following requirements:
|
||||
// - Now we should support mappings from sneak `Sneak_s` and mappings from the previous version of the plugin `(sneak-s)`
|
||||
// - 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 = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(command(keys)))
|
||||
}
|
||||
val filteredModes2 = mappingModes.filterNotTo(HashSet()) {
|
||||
VimPlugin.getKey().hasmapto(it, injector.parser.parseKeys(commandFromOriginalPlugin(keys)))
|
||||
}
|
||||
val filteredFromModes = mappingModes.filterNotTo(HashSet()) {
|
||||
injector.keyGroup.hasmapfrom(it, fromKeys)
|
||||
}
|
||||
|
||||
val doubleFiltered = mappingModes
|
||||
.filter { it in filteredModes2 && it in filteredModes && it in filteredFromModes }
|
||||
.toSet()
|
||||
putKeyMapping(doubleFiltered, fromKeys, owner, injector.parser.parseKeys(command(keys)), true)
|
||||
putKeyMapping(
|
||||
doubleFiltered,
|
||||
fromKeys,
|
||||
owner,
|
||||
injector.parser.parseKeys(commandFromOriginalPlugin(keys)),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
private fun command(keys: String) = "<Plug>(sneak-$keys)"
|
||||
private fun commandFromOriginalPlugin(keys: String) = "<Plug>Sneak_$keys"
|
@@ -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)
|
||||
}
|
||||
|
||||
|
@@ -138,7 +138,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
||||
|
||||
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||
//noinspection DuplicatedCode
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
((IjVimEditor) editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(editor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
|
@@ -267,7 +267,7 @@ public class VimIndentObject implements VimExtension {
|
||||
|
||||
final IndentObjectHandler textObjectHandler = new IndentObjectHandler(includeAbove, includeBelow);
|
||||
|
||||
if (!vimStateMachine.isOperatorPending()) {
|
||||
if (!vimStateMachine.isOperatorPending(editor.getMode())) {
|
||||
((IjVimEditor)editor).getEditor().getCaretModel().runForEachCaret((Caret caret) -> {
|
||||
final TextRange range = textObjectHandler.getRange(vimEditor, new IjVimCaret(caret), context, count, 0);
|
||||
if (range != null) {
|
||||
|
@@ -68,10 +68,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
|
||||
@@ -196,7 +197,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 +235,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 +394,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 +450,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 +484,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)
|
||||
@@ -531,7 +531,7 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
val soff = editor.getLineStartOffset(l)
|
||||
val eoff = editor.getLineEndOffset(l, true)
|
||||
val woff = injector.motion.moveCaretToLineStartSkipLeading(editor, l)
|
||||
val col = editor.offsetToVisualPosition(woff).column
|
||||
val col = editor.offsetToBufferPosition(woff).column
|
||||
val limit = max(0.0, (col + dir * indentConfig.getTotalIndent(count)).toDouble())
|
||||
.toInt()
|
||||
if (col > 0 || soff != eoff) {
|
||||
@@ -573,48 +573,62 @@ public class ChangeGroup : VimChangeGroupBase() {
|
||||
}
|
||||
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
|
||||
*
|
||||
|
@@ -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,6 +11,9 @@ 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;
|
||||
@@ -22,7 +25,10 @@ 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.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;
|
||||
@@ -34,10 +40,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -204,7 +210,8 @@ 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);
|
||||
@@ -228,7 +235,7 @@ public class EditorGroup implements PersistentStateComponent<Element>, VimEditor
|
||||
// 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));
|
||||
};
|
||||
@@ -246,14 +253,13 @@ 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);
|
||||
UserDataManager.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(new IjVimEditor(editor));
|
||||
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
||||
CaretVisualAttributesHelperKt.removeCaretsVisualAttributes(editor);
|
||||
}
|
||||
|
||||
@@ -284,6 +290,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();
|
||||
|
||||
@@ -324,20 +342,45 @@ 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 current context.
|
||||
final ClientAppSession localSession = ClientSessionsManager.getAppSessions(ClientKind.LOCAL).get(0);
|
||||
return localSession.getService(ClientEditorManager.class).editors();
|
||||
}
|
||||
}
|
||||
|
@@ -22,16 +22,17 @@ import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.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;
|
||||
@@ -40,10 +41,13 @@ import com.maddyhome.idea.vim.helper.SearchHelper;
|
||||
import com.maddyhome.idea.vim.newapi.ExecuteExtensionKt;
|
||||
import com.maddyhome.idea.vim.newapi.IjEditorExecutionContext;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine;
|
||||
import com.maddyhome.idea.vim.state.mode.Mode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
@@ -438,14 +442,35 @@ public class FileGroup extends VimFileBase {
|
||||
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.
|
||||
* Respond to editor tab selection and remember the last used tab
|
||||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VimEditor selectEditor(@NotNull String projectId, @NotNull String documentPath, @Nullable String protocol) {
|
||||
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
|
||||
if (fileSystem == null) return null;
|
||||
VirtualFile virtualFile = fileSystem.findFileByPath(documentPath);
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
Project project = Arrays.stream(ProjectManager.getInstance().getOpenProjects())
|
||||
.filter(p -> injector.getFile().getProjectId(p).equals(projectId))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
Editor editor = selectEditor(project, virtualFile);
|
||||
if (editor == null) return null;
|
||||
return new IjVimEditor(editor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getProjectId(@NotNull Object project) {
|
||||
if (!(project instanceof Project)) throw new IllegalArgumentException();
|
||||
return ((Project) project).getName();
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.options.OptionAccessScope
|
||||
*/
|
||||
@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)
|
||||
@@ -29,15 +28,15 @@ public open class GlobalIjOptions(scope: OptionAccessScope) : OptionsPropertiesB
|
||||
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)
|
||||
public var colorfulmodewidget: Boolean by optionProperty(IjOptions.colorfulmodewidget)
|
||||
|
||||
// Temporary options to control work-in-progress behaviour
|
||||
public var closenotebooks: Boolean by optionProperty(IjOptions.closenotebooks)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var oldundo: Boolean by optionProperty(IjOptions.oldundo)
|
||||
public var unifyjumps: Boolean by optionProperty(IjOptions.unifyjumps)
|
||||
public var exCommandAnnotation: Boolean by optionProperty(IjOptions.exCommandAnnotation)
|
||||
public var useNewRegex: Boolean by optionProperty(IjOptions.useNewRegex)
|
||||
public var vimscriptFunctionAnnotation: Boolean by optionProperty(IjOptions.vimscriptFunctionAnnotation)
|
||||
public var commandOrMotionAnnotation: Boolean by optionProperty(IjOptions.commandOrMotionAnnotation)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -33,8 +33,6 @@ 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(
|
||||
StringOption("ide", GLOBAL, "ide", ApplicationNamesInfo.getInstance().fullProductNameWithEdition)
|
||||
)
|
||||
@@ -81,13 +79,16 @@ public object IjOptions {
|
||||
"<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 colorfulmodewidget: ToggleOption = addOption(ToggleOption("colorfulmodewidget", GLOBAL, "colorfulmodewidget", false, isTemporary = true))
|
||||
|
||||
// Temporary feature flags during development, not really intended for external use
|
||||
public val closenotebooks: ToggleOption = addOption(ToggleOption("closenotebooks", GLOBAL, "closenotebooks", true, isHidden = true))
|
||||
public val commandOrMotionAnnotation: ToggleOption = addOption(ToggleOption("commandormotionannotation", GLOBAL, "commandormotionannotation", true, isHidden = true))
|
||||
public val exCommandAnnotation: ToggleOption = addOption(ToggleOption("excommandannotation", GLOBAL, "excommandannotation", true, isHidden = true))
|
||||
public val oldundo: ToggleOption = addOption(ToggleOption("oldundo", GLOBAL, "oldundo", true, isHidden = true))
|
||||
public val unifyjumps: ToggleOption = addOption(ToggleOption("unifyjumps", GLOBAL, "unifyjumps", true, isHidden = true))
|
||||
public val useNewRegex: ToggleOption = addOption(ToggleOption("usenewregex", GLOBAL, "usenewregex", true, isHidden = true))
|
||||
public val vimscriptFunctionAnnotation: ToggleOption = addOption(ToggleOption("vimscriptfunctionannotation", GLOBAL, "vimscriptfunctionannotation", true, isHidden = true))
|
||||
|
||||
// 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
|
||||
public 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)
|
||||
}
|
||||
}
|
@@ -24,17 +24,16 @@ import com.intellij.openapi.keymap.KeymapManager;
|
||||
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
|
||||
import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.ComplicatedKeysAction;
|
||||
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.handler.EditorActionHandlerBase;
|
||||
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.IjVimActionsInitiator;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import kotlin.Pair;
|
||||
import kotlin.text.StringsKt;
|
||||
@@ -49,6 +48,7 @@ import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
/**
|
||||
@@ -101,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,63 +221,20 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
|
||||
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
|
||||
|
||||
for (MappingMode mappingMode : command.getModes()) {
|
||||
Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
|
||||
Node<LazyVimCommand> node = getKeyRoot(mappingMode);
|
||||
NodesKt.addLeafs(node, keyStrokes, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void registerCommandAction(@NotNull VimActionsInitiator actionHolder) {
|
||||
IjVimActionsInitiator holder = (IjVimActionsInitiator)actionHolder;
|
||||
|
||||
if (!VimPlugin.getPluginId().equals(holder.getBean().getPluginDescriptor().getPluginId())) {
|
||||
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
|
||||
"Please create a plugin using `VimExtension`. " +
|
||||
"Plugin to blame: " +
|
||||
holder.getBean().getPluginDescriptor().getPluginId());
|
||||
return;
|
||||
}
|
||||
|
||||
Set<List<KeyStroke>> actionKeys = holder.getBean().getParsedKeys();
|
||||
if (actionKeys == null) {
|
||||
final EditorActionHandlerBase action = actionHolder.getInstance();
|
||||
if (action instanceof ComplicatedKeysAction) {
|
||||
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
Set<MappingMode> actionModes = holder.getBean().getParsedModes();
|
||||
if (actionModes == null) {
|
||||
throw new RuntimeException("Cannot register action: " + holder.getBean().getImplementation());
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
initIdentityChecker();
|
||||
for (List<KeyStroke> keys : actionKeys) {
|
||||
checkCommand(actionModes, actionHolder.getInstance(), keys);
|
||||
}
|
||||
}
|
||||
|
||||
for (List<KeyStroke> keyStrokes : actionKeys) {
|
||||
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);
|
||||
|
||||
for (MappingMode mappingMode : actionModes) {
|
||||
Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
|
||||
NodesKt.addLeafs(node, keyStrokes, actionHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
|
||||
for (KeyStroke key : keys) {
|
||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED &&
|
||||
!(key.getKeyCode() == KeyEvent.VK_ESCAPE && key.getModifiers() == 0) &&
|
||||
!(key.getKeyCode() == KeyEvent.VK_ENTER && key.getModifiers() == 0)) {
|
||||
getRequiredShortcutKeys().add(new RequiredShortcut(key, owner));
|
||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
||||
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
|
||||
}
|
||||
|
@@ -1,138 +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.ide.bookmark.LineBookmark;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.mark.IntellijMark;
|
||||
import com.maddyhome.idea.vim.mark.Jump;
|
||||
import com.maddyhome.idea.vim.mark.Mark;
|
||||
import com.maddyhome.idea.vim.newapi.IjVimEditor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Deprecated
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "2.3")
|
||||
public class MarkGroup {
|
||||
public List<Jump> jumps = VimInjectorKt.injector.getJumpService().getJumps("");
|
||||
|
||||
public void saveJumpLocation(@NotNull Editor editor) {
|
||||
VimInjectorKt.injector.getJumpService().saveJumpLocation(new IjVimEditor(editor));
|
||||
}
|
||||
|
||||
public void saveJumpLocation(@NotNull VimEditor editor) {
|
||||
VimInjectorKt.injector.getJumpService().saveJumpLocation(editor);
|
||||
}
|
||||
|
||||
public void setChangeMarks(@NotNull VimEditor vimEditor, @NotNull TextRange range) {
|
||||
VimMarkService markService = VimInjectorKt.injector.getMarkService();
|
||||
VimMarkServiceKt.setChangeMarks(markService, vimEditor.primaryCaret(), range);
|
||||
}
|
||||
|
||||
public void addJump(@NotNull VimEditor editor, boolean reset) {
|
||||
VimJumpServiceKt.addJump(VimInjectorKt.injector.getJumpService(), editor, reset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Mark getMark(@NotNull VimEditor editor, char ch) {
|
||||
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Jump getJump(int count) {
|
||||
return VimInjectorKt.injector.getJumpService().getJump("", count);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Mark createSystemMark(char ch, int line, int col, @NotNull VimEditor editor) {
|
||||
Editor ijEditor = ((IjVimEditor)editor).getEditor();
|
||||
@Nullable LineBookmark systemMark = SystemMarks.createOrGetSystemMark(ch, line, ijEditor);
|
||||
if (systemMark == null) {
|
||||
return null;
|
||||
}
|
||||
return new IntellijMark(systemMark, col, ijEditor.getProject());
|
||||
}
|
||||
|
||||
public boolean setMark(@NotNull VimEditor editor, char ch, int offset) {
|
||||
return VimInjectorKt.injector.getMarkService().setMark(editor.primaryCaret(), ch, offset);
|
||||
}
|
||||
|
||||
public boolean setMark(@NotNull VimEditor editor, char ch) {
|
||||
return VimInjectorKt.injector.getMarkService().setMark(editor, ch);
|
||||
}
|
||||
|
||||
public void includeCurrentCommandAsNavigation(@NotNull VimEditor editor) {
|
||||
VimInjectorKt.injector.getJumpService().includeCurrentCommandAsNavigation(editor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Mark getFileMark(@NotNull VimEditor editor, char ch) {
|
||||
return VimInjectorKt.injector.getMarkService().getMark(editor.primaryCaret(), ch);
|
||||
}
|
||||
|
||||
public void setVisualSelectionMarks(@NotNull VimEditor editor, @NotNull TextRange range) {
|
||||
VimMarkService markService = VimInjectorKt.injector.getMarkService();
|
||||
VimMarkServiceKt.setVisualSelectionMarks(markService, editor.primaryCaret(), range);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TextRange getChangeMarks(@NotNull VimEditor editor) {
|
||||
return VimInjectorKt.injector.getMarkService().getChangeMarks(editor.primaryCaret());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TextRange getVisualSelectionMarks(@NotNull VimEditor editor) {
|
||||
return VimInjectorKt.injector.getMarkService().getVisualSelectionMarks(editor.primaryCaret());
|
||||
}
|
||||
|
||||
public void resetAllMarks() {
|
||||
VimInjectorKt.injector.getMarkService().resetAllMarks();
|
||||
}
|
||||
|
||||
public void removeMark(char ch, @NotNull Mark mark) {
|
||||
VimInjectorKt.injector.getMarkService().removeMark(ch, mark);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Mark> getMarks(@NotNull VimEditor editor) {
|
||||
Set<Mark> marks = VimInjectorKt.injector.getMarkService().getAllLocalMarks(editor.primaryCaret());
|
||||
marks.addAll(VimInjectorKt.injector.getMarkService().getGlobalMarks(editor));
|
||||
return new ArrayList<>(marks);
|
||||
}
|
||||
|
||||
public int getJumpSpot() {
|
||||
return VimInjectorKt.injector.getJumpService().getJumpSpot("");
|
||||
}
|
||||
|
||||
public void updateMarkFromDelete(@Nullable VimEditor editor,
|
||||
@Nullable HashMap<Character, Mark> marks,
|
||||
int delStartOff,
|
||||
int delLength) {
|
||||
VimInjectorKt.injector.getMarkService().updateMarksFromDelete(editor, delStartOff, delLength);
|
||||
}
|
||||
|
||||
public void updateMarkFromInsert(@Nullable VimEditor editor,
|
||||
@Nullable HashMap<Character, Mark> marks,
|
||||
int insStartOff,
|
||||
int insLength) {
|
||||
VimInjectorKt.injector.getMarkService().updateMarksFromInsert(editor, insStartOff, insLength);
|
||||
}
|
||||
|
||||
public void dropLastJump() {
|
||||
VimInjectorKt.injector.getJumpService().dropLastJump("");
|
||||
}
|
||||
}
|
@@ -8,43 +8,27 @@
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.VisualPosition
|
||||
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.EditorWindow
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import com.intellij.util.MathUtil.clamp
|
||||
import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.BufferPosition
|
||||
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.VimChangeGroupBase
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimMotionGroupBase
|
||||
import com.maddyhome.idea.vim.api.addJump
|
||||
import com.maddyhome.idea.vim.api.anyNonWhitespace
|
||||
import com.maddyhome.idea.vim.api.getJump
|
||||
import com.maddyhome.idea.vim.api.getJumpSpot
|
||||
import com.maddyhome.idea.vim.api.getLeadingCharacterOffset
|
||||
import com.maddyhome.idea.vim.api.getVisualLineCount
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.api.lineLength
|
||||
import com.maddyhome.idea.vim.api.normalizeColumn
|
||||
import com.maddyhome.idea.vim.api.normalizeLine
|
||||
import com.maddyhome.idea.vim.api.normalizeOffset
|
||||
import com.maddyhome.idea.vim.api.normalizeVisualColumn
|
||||
import com.maddyhome.idea.vim.api.normalizeVisualLine
|
||||
import com.maddyhome.idea.vim.api.options
|
||||
import com.maddyhome.idea.vim.api.visualLineToBufferLine
|
||||
import com.maddyhome.idea.vim.command.Argument
|
||||
import com.maddyhome.idea.vim.command.MotionType
|
||||
@@ -53,12 +37,9 @@ import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.handler.Motion
|
||||
import com.maddyhome.idea.vim.handler.Motion.AbsoluteOffset
|
||||
import com.maddyhome.idea.vim.handler.Motion.AdjustedOffset
|
||||
import com.maddyhome.idea.vim.handler.MotionActionHandler
|
||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||
import com.maddyhome.idea.vim.handler.toMotionOrError
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.SearchHelper
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.fileSize
|
||||
import com.maddyhome.idea.vim.helper.getNormalizedScrollOffset
|
||||
@@ -66,46 +47,25 @@ import com.maddyhome.idea.vim.helper.getNormalizedSideScrollOffset
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||
import com.maddyhome.idea.vim.listener.AppCodeTemplates
|
||||
import com.maddyhome.idea.vim.mark.Mark
|
||||
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.newapi.vim
|
||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
||||
import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import org.jetbrains.annotations.Range
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This handles all motion related commands and marks
|
||||
*/
|
||||
@Service
|
||||
internal class MotionGroup : VimMotionGroupBase() {
|
||||
override fun onAppCodeMovement(editor: VimEditor, caret: VimCaret, offset: Int, oldOffset: Int) {
|
||||
AppCodeTemplates.onMovement(editor.ij, caret.ij, oldOffset < offset)
|
||||
}
|
||||
|
||||
private fun selectEditor(project: Project, mark: Mark): Editor? {
|
||||
val virtualFile = markToVirtualFile(mark) ?: return null
|
||||
return selectEditor(project, virtualFile)
|
||||
}
|
||||
|
||||
private fun markToVirtualFile(mark: Mark): VirtualFile? {
|
||||
val protocol = mark.protocol
|
||||
val fileSystem: VirtualFileSystem? = VirtualFileManager.getInstance().getFileSystem(protocol)
|
||||
return fileSystem?.findFileByPath(mark.filepath)
|
||||
}
|
||||
|
||||
private fun selectEditor(project: Project?, file: VirtualFile) =
|
||||
VimPlugin.getFile().selectEditor(project, file)
|
||||
|
||||
override fun moveCaretToMatchingPair(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
return SearchHelper.findMatchingPairOnCurrentLine(editor.ij, caret.ij).toMotionOrError()
|
||||
}
|
||||
|
||||
override fun moveCaretToFirstDisplayLine(
|
||||
editor: VimEditor,
|
||||
caret: ImmutableVimCaret,
|
||||
@@ -128,85 +88,12 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
return moveCaretToScreenLocation(editor.ij, caret.ij, ScreenLocation.MIDDLE, 0, false)
|
||||
}
|
||||
|
||||
override fun moveCaretToMark(caret: ImmutableVimCaret, ch: Char, toLineStart: Boolean): Motion {
|
||||
val markService = injector.markService
|
||||
val mark = markService.getMark(caret, ch) ?: return Motion.Error
|
||||
|
||||
val caretEditor = caret.editor
|
||||
val caretVirtualFile = EditorHelper.getVirtualFile((caretEditor as IjVimEditor).editor)
|
||||
|
||||
val line = mark.line
|
||||
|
||||
if (caretVirtualFile!!.path == mark.filepath) {
|
||||
val offset = if (toLineStart) {
|
||||
moveCaretToLineStartSkipLeading(caretEditor, line)
|
||||
} else {
|
||||
caretEditor.bufferPositionToOffset(BufferPosition(line, mark.col, false))
|
||||
}
|
||||
return offset.toMotionOrError()
|
||||
}
|
||||
|
||||
val project = caretEditor.editor.project
|
||||
val markEditor = selectEditor(project!!, mark)
|
||||
if (markEditor != null) {
|
||||
// todo should we move all the carets or only one?
|
||||
for (carett in markEditor.caretModel.allCarets) {
|
||||
val offset = if (toLineStart) {
|
||||
moveCaretToLineStartSkipLeading(IjVimEditor(markEditor), line)
|
||||
} else {
|
||||
// todo should it be the same as getting offset above?
|
||||
markEditor.logicalPositionToOffset(LogicalPosition(line, mark.col))
|
||||
}
|
||||
IjVimCaret(carett!!).moveToOffset(offset)
|
||||
}
|
||||
}
|
||||
return Motion.Error
|
||||
}
|
||||
|
||||
override fun moveCaretToJump(editor: VimEditor, caret: ImmutableVimCaret, count: Int): Motion {
|
||||
val jumpService = injector.jumpService
|
||||
val spot = jumpService.getJumpSpot(editor)
|
||||
val (line, col, fileName) = jumpService.getJump(editor, count) ?: return Motion.Error
|
||||
val vf = EditorHelper.getVirtualFile(editor.ij) ?: return Motion.Error
|
||||
val lp = BufferPosition(line, col, false)
|
||||
val lpNative = LogicalPosition(line, col, false)
|
||||
return if (vf.path != fileName) {
|
||||
val newFile = LocalFileSystem.getInstance().findFileByPath(fileName.replace(File.separatorChar, '/'))
|
||||
?: return Motion.Error
|
||||
selectEditor(editor.ij.project, newFile)?.let { newEditor ->
|
||||
if (spot == -1) {
|
||||
jumpService.addJump(editor, false)
|
||||
}
|
||||
newEditor.vim.let {
|
||||
it.currentCaret().moveToOffset(it.normalizeOffset(newEditor.logicalPositionToOffset(lpNative), false))
|
||||
}
|
||||
}
|
||||
Motion.Error
|
||||
} else {
|
||||
if (spot == -1) {
|
||||
jumpService.addJump(editor, false)
|
||||
}
|
||||
editor.bufferPositionToOffset(lp).toMotionOrError()
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveCaretToCurrentDisplayLineMiddle(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
val width = EditorHelper.getApproximateScreenWidth(editor.ij) / 2
|
||||
val len = editor.lineLength(editor.currentCaret().getBufferPosition().line)
|
||||
return moveCaretToColumn(editor, caret, max(0, min(len - 1, width)), false)
|
||||
}
|
||||
|
||||
override fun moveCaretToColumn(editor: VimEditor, caret: ImmutableVimCaret, count: Int, allowEnd: Boolean): Motion {
|
||||
val line = caret.getLine().line
|
||||
val column = editor.normalizeColumn(line, count, allowEnd)
|
||||
val offset = editor.bufferPositionToOffset(BufferPosition(line, column, false))
|
||||
return if (column != count) {
|
||||
AdjustedOffset(offset, count)
|
||||
} else {
|
||||
AbsoluteOffset(offset)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveCaretToCurrentDisplayLineStart(editor: VimEditor, caret: ImmutableVimCaret): Motion {
|
||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||
return moveCaretToColumn(editor, caret, col, false)
|
||||
@@ -217,7 +104,7 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
val col = EditorHelper.getVisualColumnAtLeftOfDisplay(editor.ij, caret.getVisualPosition().line)
|
||||
val bufferLine = caret.getLine().line
|
||||
val bufferLine = caret.getLine()
|
||||
return editor.getLeadingCharacterOffset(bufferLine, col)
|
||||
}
|
||||
|
||||
@@ -230,36 +117,6 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
return moveCaretToColumn(editor, caret, col, allowEnd)
|
||||
}
|
||||
|
||||
override fun moveCaretToLineWithSameColumn(
|
||||
editor: VimEditor,
|
||||
line: Int,
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
var c = caret.vimLastColumn
|
||||
var l = line
|
||||
if (l < 0) {
|
||||
l = 0
|
||||
c = 0
|
||||
} else if (l >= editor.lineCount()) {
|
||||
l = editor.normalizeLine(editor.lineCount() - 1)
|
||||
c = editor.lineLength(l)
|
||||
}
|
||||
val newPos = BufferPosition(l, editor.normalizeColumn(l, c, false))
|
||||
return editor.bufferPositionToOffset(newPos)
|
||||
}
|
||||
|
||||
override fun moveCaretToLineWithStartOfLineOption(
|
||||
editor: VimEditor,
|
||||
line: Int,
|
||||
caret: ImmutableVimCaret,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
return if (injector.options(editor).startofline) {
|
||||
moveCaretToLineStartSkipLeading(editor, line)
|
||||
} else {
|
||||
moveCaretToLineWithSameColumn(editor, line, caret)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
|
||||
*/
|
||||
@@ -277,30 +134,18 @@ internal class MotionGroup : VimMotionGroupBase() {
|
||||
}
|
||||
|
||||
override fun moveCaretGotoPreviousTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||
switchEditorTab(currentWindow, if (rawCount >= 1) -rawCount else -1, false)
|
||||
return editor.currentCaret().offset.point
|
||||
return editor.currentCaret().offset
|
||||
}
|
||||
|
||||
override fun moveCaretGotoNextTab(editor: VimEditor, context: ExecutionContext, rawCount: Int): Int {
|
||||
val absolute = rawCount >= 1
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset.point
|
||||
val project = editor.ij.project ?: return editor.currentCaret().offset
|
||||
val currentWindow = FileEditorManagerEx.getInstanceEx(project).splitters.currentWindow
|
||||
switchEditorTab(currentWindow, if (absolute) rawCount - 1 else 1, absolute)
|
||||
return editor.currentCaret().offset.point
|
||||
}
|
||||
|
||||
override fun moveCaretToLinePercent(
|
||||
editor: VimEditor,
|
||||
caret: ImmutableVimCaret,
|
||||
count: Int,
|
||||
): @Range(from = 0, to = Int.MAX_VALUE.toLong()) Int {
|
||||
return moveCaretToLineWithStartOfLineOption(
|
||||
editor,
|
||||
editor.normalizeLine((editor.lineCount() * clamp(count, 0, 100) + 99) / 100 - 1),
|
||||
caret,
|
||||
)
|
||||
return editor.currentCaret().offset
|
||||
}
|
||||
|
||||
private enum class ScreenLocation {
|
||||
|
@@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.KeyboardShortcut
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.ide.CopyPasteManager
|
||||
import com.intellij.openapi.keymap.KeymapUtil
|
||||
@@ -55,6 +56,7 @@ import javax.swing.KeyStroke
|
||||
* This service is can be used as application level and as project level service.
|
||||
* If project is null, this means that this is an application level service and notification will be shown for all projects
|
||||
*/
|
||||
@Service(Service.Level.PROJECT, Service.Level.APP)
|
||||
internal class NotificationService(private val project: Project?) {
|
||||
// This constructor is used to create an applicationService
|
||||
@Suppress("unused")
|
||||
@@ -276,7 +278,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
ActionTracker.logTrackedAction(id)
|
||||
ActionTracker.Util.logTrackedAction(id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +286,7 @@ internal class NotificationService(private val project: Project?) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
CopyPasteManager.getInstance().setContents(StringSelection(id ?: ""))
|
||||
if (id != null) {
|
||||
ActionTracker.logCopiedAction(id)
|
||||
ActionTracker.Util.logCopiedAction(id)
|
||||
}
|
||||
notification?.expire()
|
||||
|
||||
|
@@ -65,28 +65,28 @@ internal class OptionGroup : VimOptionGroupBase(), IjVimOptionGroup {
|
||||
}
|
||||
}
|
||||
|
||||
internal class IjOptionConstants {
|
||||
public class IjOptionConstants {
|
||||
@Suppress("SpellCheckingInspection", "MemberVisibilityCanBePrivate", "ConstPropertyName")
|
||||
companion object {
|
||||
public companion object {
|
||||
|
||||
const val idearefactormode_keep = "keep"
|
||||
const val idearefactormode_select = "select"
|
||||
const val idearefactormode_visual = "visual"
|
||||
public const val idearefactormode_keep: String = "keep"
|
||||
public const val idearefactormode_select: String = "select"
|
||||
public const val idearefactormode_visual: String = "visual"
|
||||
|
||||
const val ideastatusicon_enabled = "enabled"
|
||||
const val ideastatusicon_gray = "gray"
|
||||
const val ideastatusicon_disabled = "disabled"
|
||||
public const val ideastatusicon_enabled: String = "enabled"
|
||||
public const val ideastatusicon_gray: String = "gray"
|
||||
public const val ideastatusicon_disabled: String = "disabled"
|
||||
|
||||
const val ideavimsupport_dialog = "dialog"
|
||||
const val ideavimsupport_singleline = "singleline"
|
||||
const val ideavimsupport_dialoglegacy = "dialoglegacy"
|
||||
public const val ideavimsupport_dialog: String = "dialog"
|
||||
public const val ideavimsupport_singleline: String = "singleline"
|
||||
public const val ideavimsupport_dialoglegacy: String = "dialoglegacy"
|
||||
|
||||
const val ideawrite_all = "all"
|
||||
const val ideawrite_file = "file"
|
||||
public const val ideawrite_all: String = "all"
|
||||
public const val ideawrite_file: String = "file"
|
||||
|
||||
val ideaStatusIconValues = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
||||
val ideaRefactorModeValues = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
||||
val ideaWriteValues = setOf(ideawrite_all, ideawrite_file)
|
||||
val ideavimsupportValues = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
||||
public val ideaStatusIconValues: Set<String> = setOf(ideastatusicon_enabled, ideastatusicon_gray, ideastatusicon_disabled)
|
||||
public val ideaRefactorModeValues: Set<String> = setOf(idearefactormode_keep, idearefactormode_select, idearefactormode_visual)
|
||||
public val ideaWriteValues: Set<String> = setOf(ideawrite_all, ideawrite_file)
|
||||
public val ideavimsupportValues: Set<String> = setOf(ideavimsupport_dialog, ideavimsupport_singleline, ideavimsupport_dialoglegacy)
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.util.execution.ParametersListUtil
|
||||
import com.intellij.util.text.CharSequenceReader
|
||||
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
||||
import com.maddyhome.idea.vim.KeyProcessResult
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
@@ -37,7 +38,6 @@ import com.maddyhome.idea.vim.state.mode.Mode
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.NORMAL
|
||||
import com.maddyhome.idea.vim.state.mode.Mode.VISUAL
|
||||
import com.maddyhome.idea.vim.state.mode.ReturnableFromCmd
|
||||
import com.maddyhome.idea.vim.state.mode.mode
|
||||
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.vimscript.model.CommandLineVimLContext
|
||||
import java.io.BufferedWriter
|
||||
@@ -51,6 +51,8 @@ import javax.swing.SwingUtilities
|
||||
public class ProcessGroup : VimProcessGroupBase() {
|
||||
override var lastCommand: String? = null
|
||||
private set
|
||||
override var isCommandProcessing: Boolean = false
|
||||
override var modeBeforeCommandProcessing: Mode? = null
|
||||
|
||||
public override fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char) {
|
||||
// Don't allow searching in one line editors
|
||||
@@ -79,26 +81,31 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
"Cannot enable cmd mode from current mode $currentMode"
|
||||
}
|
||||
|
||||
isCommandProcessing = true
|
||||
modeBeforeCommandProcessing = currentMode
|
||||
val initText = getRange(editor, cmd)
|
||||
injector.markService.setVisualSelectionMarks(editor)
|
||||
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||
}
|
||||
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean {
|
||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
||||
// is open. So I'll put focus back in the editor and process the key.
|
||||
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
if (panel.isActive) {
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
|
||||
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||
requestFocus(panel.entry)
|
||||
panel.handleKey(stroke)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
getInstance(editor).mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||
lambdaEditor.mode = NORMAL()
|
||||
getInstance().reset(lambdaEditor)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -108,7 +115,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
panel.deactivate(true)
|
||||
var res = true
|
||||
try {
|
||||
getInstance(editor).mode = NORMAL()
|
||||
editor.mode = NORMAL()
|
||||
|
||||
logger.debug("processing command")
|
||||
|
||||
@@ -134,6 +141,9 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
logger.error(bad)
|
||||
VimPlugin.indicateError()
|
||||
res = false
|
||||
} finally {
|
||||
isCommandProcessing = false
|
||||
modeBeforeCommandProcessing = null
|
||||
}
|
||||
|
||||
return res
|
||||
@@ -145,7 +155,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
}
|
||||
|
||||
public override fun cancelExEntry(editor: VimEditor, resetCaret: Boolean) {
|
||||
editor.vimStateMachine.mode = NORMAL()
|
||||
editor.mode = NORMAL()
|
||||
getInstance().reset(editor)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.deactivate(true, resetCaret)
|
||||
@@ -155,7 +165,7 @@ public class ProcessGroup : VimProcessGroupBase() {
|
||||
val initText = getRange(editor, cmd) + "!"
|
||||
val currentMode = editor.mode
|
||||
check(currentMode is ReturnableFromCmd) { "Cannot enable cmd mode from $currentMode" }
|
||||
editor.vimStateMachine.mode = Mode.CMD_LINE(currentMode)
|
||||
editor.mode = Mode.CMD_LINE(currentMode)
|
||||
val panel = ExEntryPanel.getInstance()
|
||||
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||
}
|
||||
|
@@ -14,9 +14,9 @@ import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||
import com.maddyhome.idea.vim.register.Register;
|
||||
import com.maddyhome.idea.vim.register.VimRegisterGroupBase;
|
||||
import com.maddyhome.idea.vim.state.mode.SelectionType;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -37,6 +37,10 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
|
||||
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class);
|
||||
|
||||
public RegisterGroup() {
|
||||
this.initClipboardOptionListener();
|
||||
}
|
||||
|
||||
public void saveData(final @NotNull Element element) {
|
||||
logger.debug("Save registers data");
|
||||
final Element registersElement = new Element("registers");
|
||||
|
@@ -21,8 +21,6 @@ import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.api.*;
|
||||
@@ -33,9 +31,7 @@ import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.ranges.LineRange;
|
||||
import com.maddyhome.idea.vim.helper.*;
|
||||
import com.maddyhome.idea.vim.history.HistoryConstants;
|
||||
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.*;
|
||||
import com.maddyhome.idea.vim.options.GlobalOptionChangeListener;
|
||||
import com.maddyhome.idea.vim.regexp.CharPointer;
|
||||
import com.maddyhome.idea.vim.regexp.CharacterClasses;
|
||||
@@ -60,36 +56,47 @@ import java.text.ParsePosition;
|
||||
import java.util.*;
|
||||
|
||||
import static com.maddyhome.idea.vim.api.VimInjectorKt.*;
|
||||
import static com.maddyhome.idea.vim.helper.HelperKt.localEditors;
|
||||
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
|
||||
import static com.maddyhome.idea.vim.newapi.IjVimInjectorKt.globalIjOptions;
|
||||
import static com.maddyhome.idea.vim.register.RegisterConstants.LAST_SEARCH_REGISTER;
|
||||
|
||||
@State(name = "VimSearchSettings", storages = {
|
||||
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
|
||||
})
|
||||
public class SearchGroup extends VimSearchGroupBase implements PersistentStateComponent<Element> {
|
||||
@Deprecated
|
||||
/**
|
||||
* @deprecated Replace with IjVimSearchGroup
|
||||
*/
|
||||
public class SearchGroup extends IjVimSearchGroup implements PersistentStateComponent<Element> {
|
||||
public SearchGroup() {
|
||||
// TODO: Investigate migrating these listeners to use the effective value change listener
|
||||
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating
|
||||
// the highlights in that project's current document's open editors (see VIM-2779).
|
||||
// However, we probably only want to update the editors associated with the current document, so maybe the whole
|
||||
// code needs to be reworked. We're currently using the same update code for changes in the search term as well as
|
||||
// changes in the search options.
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.hlsearch, () -> {
|
||||
resetShowSearchHighlight();
|
||||
forceUpdateSearchHighlights();
|
||||
});
|
||||
|
||||
final GlobalOptionChangeListener updateHighlightsIfVisible = () -> {
|
||||
if (showSearchHighlight) {
|
||||
super();
|
||||
if (!globalIjOptions(injector).getUseNewRegex()) {
|
||||
// TODO: Investigate migrating these listeners to use the effective value change listener
|
||||
// This would allow us to update the editor we're told to update, rather than looping over all projects and updating
|
||||
// the highlights in that project's current document's open editors (see VIM-2779).
|
||||
// However, we probably only want to update the editors associated with the current document, so maybe the whole
|
||||
// code needs to be reworked. We're currently using the same update code for changes in the search term as well as
|
||||
// changes in the search options.
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.hlsearch, () -> {
|
||||
resetShowSearchHighlight();
|
||||
forceUpdateSearchHighlights();
|
||||
}
|
||||
};
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible);
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible);
|
||||
});
|
||||
|
||||
final GlobalOptionChangeListener updateHighlightsIfVisible = () -> {
|
||||
if (showSearchHighlight) {
|
||||
forceUpdateSearchHighlights();
|
||||
}
|
||||
};
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.ignorecase, updateHighlightsIfVisible);
|
||||
VimPlugin.getOptionGroup().addGlobalOptionChangeListener(Options.smartcase, updateHighlightsIfVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public void turnOn() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.updateSearchHighlights(false);
|
||||
return;
|
||||
}
|
||||
updateSearchHighlights();
|
||||
}
|
||||
|
||||
@@ -100,7 +107,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@Override
|
||||
public void resetState() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.resetState();
|
||||
return;
|
||||
}
|
||||
lastPatternIdx = RE_SEARCH;
|
||||
lastSearch = lastSubstitute = lastReplace = null;
|
||||
lastPatternOffset = "";
|
||||
@@ -114,7 +126,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*
|
||||
* @return The pattern used for last search. Can be null
|
||||
*/
|
||||
@Override
|
||||
public @Nullable String getLastSearchPattern() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchPattern();
|
||||
return lastSearch;
|
||||
}
|
||||
|
||||
@@ -122,7 +136,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
* Get the last pattern used in substitution.
|
||||
* @return The pattern used for the last substitute command. Can be null
|
||||
*/
|
||||
@Override
|
||||
public @Nullable String getLastSubstitutePattern() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSubstitutePattern();
|
||||
return lastSubstitute;
|
||||
}
|
||||
|
||||
@@ -131,7 +147,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*
|
||||
* @return The pattern last used for either searching or substitution. Can be null
|
||||
*/
|
||||
public @Nullable String getLastUsedPattern() {
|
||||
@Override
|
||||
protected @Nullable String getLastUsedPattern() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastUsedPattern();
|
||||
switch (lastPatternIdx) {
|
||||
case RE_SEARCH: return lastSearch;
|
||||
case RE_SUBST: return lastSubstitute;
|
||||
@@ -195,6 +213,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
@Override
|
||||
public void setLastSearchState(@SuppressWarnings("unused") @NotNull VimEditor editor, @NotNull String pattern,
|
||||
@NotNull String patternOffset, Direction direction) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.setLastSearchState(pattern, patternOffset, direction);
|
||||
return;
|
||||
}
|
||||
setLastUsedPattern(pattern, RE_SEARCH, true);
|
||||
lastIgnoreSmartCase = false;
|
||||
lastPatternOffset = patternOffset;
|
||||
@@ -226,7 +248,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
int startLine,
|
||||
int endLine,
|
||||
boolean ignoreCase) {
|
||||
return SearchHelper.findAll(editor, pattern, startLine, endLine, ignoreCase);
|
||||
return injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine, ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,6 +276,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*/
|
||||
@Override
|
||||
public int processSearchCommand(@NotNull VimEditor editor, @NotNull String command, int startOffset, @NotNull Direction dir) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchCommand(editor, command, startOffset, dir);
|
||||
|
||||
boolean isNewPattern = false;
|
||||
String pattern = null;
|
||||
String patternOffset = null;
|
||||
@@ -414,6 +438,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*/
|
||||
@Override
|
||||
public int searchWord(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count, boolean whole, @NotNull Direction dir) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.searchWord(editor, caret, count, whole, dir);
|
||||
TextRange range = SearchHelper.findWordUnderCursor(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret());
|
||||
if (range == null) {
|
||||
logger.warn("No range was found");
|
||||
@@ -455,6 +480,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*/
|
||||
@Override
|
||||
public int searchNext(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.searchNext(editor, caret, count);
|
||||
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count, lastDir);
|
||||
}
|
||||
|
||||
@@ -471,6 +497,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*/
|
||||
@Override
|
||||
public int searchPrevious(@NotNull VimEditor editor, @NotNull ImmutableVimCaret caret, int count) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.searchPrevious(editor, caret, count);
|
||||
return searchNextWithDirection(((IjVimEditor)editor).getEditor(), ((IjVimCaret)caret).getCaret(), count,
|
||||
lastDir.reverse());
|
||||
}
|
||||
@@ -511,19 +538,25 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*
|
||||
* @param editor The editor to search in
|
||||
* @param caret The caret to use for initial search offset, and to move for interactive substitution
|
||||
* @param context
|
||||
* @param range Only search and substitute within the given line range. Must be valid
|
||||
* @param excmd The command part of the ex command line, e.g. `s` or `substitute`, or `~`
|
||||
* @param exarg The argument to the substitute command, such as `/{pattern}/{string}/[flags]`
|
||||
* @return True if the substitution succeeds, false on error. Will succeed even if nothing is modified
|
||||
* @return True if the substitution succeeds, false on error. Will succeed even if nothing is modified
|
||||
*/
|
||||
@Override
|
||||
@RWLockLabel.SelfSynchronized
|
||||
public boolean processSubstituteCommand(@NotNull VimEditor editor,
|
||||
@NotNull VimCaret caret,
|
||||
@NotNull ExecutionContext context,
|
||||
@NotNull LineRange range,
|
||||
@NotNull @NonNls String excmd,
|
||||
@NotNull @NonNls String exarg,
|
||||
@NotNull VimLContext parent) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
return super.processSubstituteCommand(editor, caret, context, range, excmd, exarg, parent);
|
||||
}
|
||||
|
||||
// Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
|
||||
List<ExException> exceptions = new ArrayList<>();
|
||||
if (CommandStateHelper.inVisualMode(((IjVimEditor) editor).getEditor())) {
|
||||
@@ -687,8 +720,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
return false;
|
||||
}
|
||||
|
||||
Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat,
|
||||
RE_SUBST);
|
||||
Pair<Boolean, Triple<Object, String, Object>> booleanregmmatch_tPair = search_regcomp(pat, which_pat, RE_SUBST);
|
||||
if (!booleanregmmatch_tPair.getFirst()) {
|
||||
if (do_error) {
|
||||
VimPlugin.showMessage(MessageHelper.message(Msg.e_invcmd));
|
||||
@@ -696,9 +728,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
}
|
||||
return false;
|
||||
}
|
||||
RegExp.regmmatch_T regmatch = (RegExp.regmmatch_T) booleanregmmatch_tPair.getSecond().getFirst();
|
||||
RegExp.regmmatch_T regmatch = (RegExp.regmmatch_T)booleanregmmatch_tPair.getSecond().getFirst();
|
||||
String pattern = booleanregmmatch_tPair.getSecond().getSecond();
|
||||
RegExp sp = (RegExp) booleanregmmatch_tPair.getSecond().getThird();
|
||||
RegExp sp = (RegExp)booleanregmmatch_tPair.getSecond().getThird();
|
||||
|
||||
/* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
|
||||
if (do_ic == 'i') {
|
||||
@@ -731,8 +763,8 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
resetShowSearchHighlight();
|
||||
forceUpdateSearchHighlights();
|
||||
|
||||
int start = ((IjVimEditor) editor).getEditor().getDocument().getLineStartOffset(line1);
|
||||
int end = ((IjVimEditor) editor).getEditor().getDocument().getLineEndOffset(line2);
|
||||
int start = ((IjVimEditor)editor).getEditor().getDocument().getLineStartOffset(line1);
|
||||
int end = ((IjVimEditor)editor).getEditor().getDocument().getLineEndOffset(line2);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("search range=[" + start + "," + end + "]");
|
||||
@@ -755,7 +787,6 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
firstMatch = false;
|
||||
}
|
||||
|
||||
|
||||
String match = sp.vim_regsub_multi(regmatch, lnum, sub, 1, false);
|
||||
if (sub.charAt(0) == '\\' && sub.charAt(1) == '=') {
|
||||
String exprString = sub.toString().substring(2);
|
||||
@@ -764,22 +795,25 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
exceptions.add(new ExException("E15: Invalid expression: " + exprString));
|
||||
expression = new SimpleExpression(new VimString(""));
|
||||
}
|
||||
} else if (match == null) {
|
||||
}
|
||||
else if (match == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int line = lnum + regmatch.startpos[0].lnum;
|
||||
CharacterPosition startpos = new CharacterPosition(lnum + regmatch.startpos[0].lnum, regmatch.startpos[0].col);
|
||||
CharacterPosition endpos = new CharacterPosition(lnum + regmatch.endpos[0].lnum, regmatch.endpos[0].col);
|
||||
int startoff = startpos.toOffset(((IjVimEditor) editor).getEditor());
|
||||
int endoff = endpos.toOffset(((IjVimEditor) editor).getEditor());
|
||||
int startoff = startpos.toOffset(((IjVimEditor)editor).getEditor());
|
||||
int endoff = endpos.toOffset(((IjVimEditor)editor).getEditor());
|
||||
|
||||
if (do_all || line != lastLine) {
|
||||
boolean doReplace = true;
|
||||
if (do_ask) {
|
||||
RangeHighlighter hl = SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor) editor).getEditor(), startoff, endoff);
|
||||
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor) editor).getEditor(), match, ((IjVimCaret) caret).getCaret(), startoff);
|
||||
((IjVimEditor) editor).getEditor().getMarkupModel().removeHighlighter(hl);
|
||||
RangeHighlighter hl =
|
||||
SearchHighlightsHelper.addSubstitutionConfirmationHighlight(((IjVimEditor)editor).getEditor(), startoff,
|
||||
endoff);
|
||||
final ReplaceConfirmationChoice choice = confirmChoice(((IjVimEditor)editor).getEditor(), context, match, ((IjVimCaret)caret).getCaret(), startoff);
|
||||
((IjVimEditor)editor).getEditor().getMarkupModel().removeHighlighter(hl);
|
||||
switch (choice) {
|
||||
case SUBSTITUTE_THIS:
|
||||
doReplace = true;
|
||||
@@ -802,25 +836,25 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
}
|
||||
}
|
||||
if (doReplace) {
|
||||
SubmatchFunctionHandler.Companion.getInstance().setLatestMatch(((IjVimEditor) editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff)));
|
||||
SubmatchFunctionHandler.Companion.getInstance().setLatestMatch(
|
||||
((IjVimEditor)editor).getEditor().getDocument().getText(new com.intellij.openapi.util.TextRange(startoff, endoff)));
|
||||
caret.moveToOffset(startoff);
|
||||
if (expression != null) {
|
||||
try {
|
||||
match = expression
|
||||
.evaluate(editor, injector.getExecutionContextManager().onEditor(editor, null), parent)
|
||||
.toInsertableString();
|
||||
} catch (Exception e) {
|
||||
exceptions.add((ExException) e);
|
||||
match = expression.evaluate(editor, context, parent).toInsertableString();
|
||||
}
|
||||
catch (Exception e) {
|
||||
exceptions.add((ExException)e);
|
||||
match = "";
|
||||
}
|
||||
}
|
||||
|
||||
String finalMatch = match;
|
||||
ApplicationManager.getApplication().runWriteAction(() -> ((IjVimEditor) editor).getEditor().getDocument().replaceString(startoff, endoff,
|
||||
finalMatch));
|
||||
ApplicationManager.getApplication().runWriteAction(
|
||||
() -> ((IjVimEditor)editor).getEditor().getDocument().replaceString(startoff, endoff, finalMatch));
|
||||
lastMatch = startoff;
|
||||
int newend = startoff + match.length();
|
||||
newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor) editor).getEditor(), newend);
|
||||
newpos = CharacterPosition.Companion.fromOffset(((IjVimEditor)editor).getEditor(), newend);
|
||||
|
||||
lnum += newpos.line - endpos.line;
|
||||
line2 += newpos.line - endpos.line;
|
||||
@@ -875,6 +909,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
|
||||
@Override
|
||||
public void setLastSearchPattern(@Nullable String lastSearchPattern) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.setLastSearchPattern(lastSearchPattern);
|
||||
return;
|
||||
}
|
||||
this.lastSearch = lastSearchPattern;
|
||||
if (showSearchHighlight) {
|
||||
resetIncsearchHighlights();
|
||||
@@ -884,6 +922,10 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
|
||||
@Override
|
||||
public void setLastSubstitutePattern(@Nullable String lastSubstitutePattern) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.setLastSubstitutePattern(lastSubstitutePattern);
|
||||
return;
|
||||
}
|
||||
this.lastSubstitute = lastSubstitutePattern;
|
||||
}
|
||||
|
||||
@@ -893,6 +935,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
int patternOffset,
|
||||
int startOffset,
|
||||
@NotNull Direction direction) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.processSearchRange(editor, pattern, patternOffset, startOffset, direction);
|
||||
return processSearchRange(((IjVimEditor) editor).getEditor(), pattern, patternOffset, startOffset, direction);
|
||||
}
|
||||
|
||||
@@ -949,7 +992,9 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
return new Pair<>(true, new Triple<>(regmatch, pattern, sp));
|
||||
}
|
||||
|
||||
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor, @NotNull String match, @NotNull Caret caret, int startoff) {
|
||||
private static @NotNull ReplaceConfirmationChoice confirmChoice(@NotNull Editor editor,
|
||||
@NotNull ExecutionContext context,
|
||||
@NotNull String match, @NotNull Caret caret, int startoff) {
|
||||
final Ref<ReplaceConfirmationChoice> result = Ref.create(ReplaceConfirmationChoice.QUIT);
|
||||
final Function1<KeyStroke, Boolean> keyStrokeProcessor = key -> {
|
||||
final ReplaceConfirmationChoice choice;
|
||||
@@ -983,7 +1028,6 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
else {
|
||||
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
|
||||
final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
|
||||
ExecutionContext.Editor context = injector.getExecutionContextManager().onEditor(new IjVimEditor(editor), null);
|
||||
exEntryPanel.activate(editor, ((IjEditorExecutionContext)context).getContext(), MessageHelper.message("replace.with.0", match), "", 1);
|
||||
new IjVimCaret(caret).moveToOffset(startoff);
|
||||
ModalEntry.INSTANCE.activate(new IjVimEditor(editor), keyStrokeProcessor);
|
||||
@@ -1015,6 +1059,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
*/
|
||||
@Override
|
||||
public @Nullable TextRange getNextSearchRange(@NotNull VimEditor editor, int count, boolean forwards) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.getNextSearchRange(editor, count, forwards);
|
||||
editor.removeSecondaryCarets();
|
||||
TextRange current = findUnderCaret(editor);
|
||||
|
||||
@@ -1040,23 +1085,16 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
private @Nullable TextRange findNextSearchForGn(@NotNull VimEditor editor, int count, boolean forwards) {
|
||||
if (forwards) {
|
||||
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE);
|
||||
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset().getPoint(), count, searchOptions);
|
||||
return VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), editor.primaryCaret().getOffset(), count, searchOptions);
|
||||
} else {
|
||||
return searchBackward(editor, editor.primaryCaret().getOffset().getPoint(), count);
|
||||
return searchBackward(editor, editor.primaryCaret().getOffset(), count);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TextRange findUnderCaret(@NotNull VimEditor editor) {
|
||||
final TextRange backSearch = searchBackward(editor, editor.primaryCaret().getOffset().getPoint() + 1, 1);
|
||||
if (backSearch == null) return null;
|
||||
return backSearch.contains(editor.primaryCaret().getOffset().getPoint()) ? backSearch : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TextRange searchBackward(@NotNull VimEditor editor, int offset, int count) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.searchBackward(editor, offset, count);
|
||||
// Backward search returns wrongs end offset for some cases. That's why we should perform additional forward search
|
||||
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WRAP, SearchOptions.WHOLE_FILE, SearchOptions.BACKWARDS);
|
||||
final TextRange foundBackward = VimInjectorKt.getInjector().getSearchHelper().findPattern(editor, getLastUsedPattern(), offset, count, searchOptions);
|
||||
@@ -1074,7 +1112,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
//
|
||||
// *******************************************************************************************************************
|
||||
//region Search highlights
|
||||
@Override
|
||||
public void clearSearchHighlight() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.clearSearchHighlight();
|
||||
return;
|
||||
}
|
||||
showSearchHighlight = false;
|
||||
updateSearchHighlights();
|
||||
}
|
||||
@@ -1094,7 +1137,12 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
/**
|
||||
* Reset the search highlights to the last used pattern after highlighting incsearch results.
|
||||
*/
|
||||
@Override
|
||||
public void resetIncsearchHighlights() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.resetIncsearchHighlights();
|
||||
return;
|
||||
}
|
||||
SearchHighlightsHelper.updateSearchHighlights(getLastUsedPattern(), lastIgnoreSmartCase, showSearchHighlight, true);
|
||||
}
|
||||
|
||||
@@ -1103,9 +1151,13 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
}
|
||||
|
||||
private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.highlightSearchLines(new IjVimEditor(editor), startLine, endLine);
|
||||
return;
|
||||
}
|
||||
final String pattern = getLastUsedPattern();
|
||||
if (pattern != null) {
|
||||
final List<TextRange> results = SearchHelper.findAll(editor, pattern, startLine, endLine,
|
||||
final List<TextRange> results = injector.getSearchHelper().findAll(new IjVimEditor(editor), pattern, startLine, endLine,
|
||||
shouldIgnoreCase(pattern, lastIgnoreSmartCase));
|
||||
SearchHighlightsHelper.highlightSearchResults(editor, pattern, results, -1);
|
||||
}
|
||||
@@ -1114,12 +1166,17 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
/**
|
||||
* Updates search highlights when the selected editor changes
|
||||
*/
|
||||
public static void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) {
|
||||
public void fileEditorManagerSelectionChangedCallback(@SuppressWarnings("unused") @NotNull FileEditorManagerEvent event) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) {
|
||||
super.updateSearchHighlights(false);
|
||||
return;
|
||||
}
|
||||
VimPlugin.getSearch().updateSearchHighlights();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer findDecimalNumber(@NotNull String line) {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.findDecimalNumber(line);
|
||||
Pair<TextRange, NumberType> searchResult = SearchHelper.findNumberInText(line, 0, false, false, false);
|
||||
if (searchResult != null) {
|
||||
TextRange range = searchResult.component1();
|
||||
@@ -1131,6 +1188,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
@NotNull
|
||||
@Override
|
||||
public Direction getLastSearchDirection() {
|
||||
if (globalIjOptions(injector).getUseNewRegex()) return super.getLastSearchDirection();
|
||||
return lastDir;
|
||||
}
|
||||
|
||||
@@ -1142,47 +1200,50 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
public static DocumentSearchListener INSTANCE = new DocumentSearchListener();
|
||||
|
||||
@Contract(pure = true)
|
||||
private DocumentSearchListener () {
|
||||
private DocumentSearchListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void documentChanged(@NotNull DocumentEvent event) {
|
||||
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
|
||||
final Document document = event.getDocument();
|
||||
// Loop over all local editors for the changed document, across all projects, and update search highlights.
|
||||
// Note that the change may have come from a remote guest in Code With Me scenarios (in which case
|
||||
// ClientId.current will be a guest ID), but we don't care - we still need to add/remove highlights for the
|
||||
// changed text. Make sure we only update local editors, though.
|
||||
final Document document = event.getDocument();
|
||||
for (VimEditor vimEditor : injector.getEditorGroup().getEditors(new IjVimDocument(document))) {
|
||||
final Editor editor = ((IjVimEditor)vimEditor).getEditor();
|
||||
Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
if (hls == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Editor editor : localEditors(document, project)) {
|
||||
Collection<RangeHighlighter> hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
if (hls == null) {
|
||||
continue;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("hls=" + hls);
|
||||
logger.debug("event=" + event);
|
||||
}
|
||||
|
||||
// We can only re-highlight whole lines, so clear any highlights in the affected lines
|
||||
final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
|
||||
final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
|
||||
final int startLineOffset = document.getLineStartOffset(startPosition.line);
|
||||
final int endLineOffset = document.getLineEndOffset(endPosition.line);
|
||||
|
||||
final Iterator<RangeHighlighter> iter = hls.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RangeHighlighter highlighter = iter.next();
|
||||
if (!highlighter.isValid() ||
|
||||
(highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
|
||||
iter.remove();
|
||||
editor.getMarkupModel().removeHighlighter(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("hls=" + hls);
|
||||
logger.debug("event=" + event);
|
||||
}
|
||||
VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
|
||||
|
||||
// We can only re-highlight whole lines, so clear any highlights in the affected lines
|
||||
final LogicalPosition startPosition = editor.offsetToLogicalPosition(event.getOffset());
|
||||
final LogicalPosition endPosition = editor.offsetToLogicalPosition(event.getOffset() + event.getNewLength());
|
||||
final int startLineOffset = document.getLineStartOffset(startPosition.line);
|
||||
final int endLineOffset = document.getLineEndOffset(endPosition.line);
|
||||
|
||||
final Iterator<RangeHighlighter> iter = hls.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final RangeHighlighter highlighter = iter.next();
|
||||
if (!highlighter.isValid() || (highlighter.getStartOffset() >= startLineOffset && highlighter.getEndOffset() <= endLineOffset)) {
|
||||
iter.remove();
|
||||
editor.getMarkupModel().removeHighlighter(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
VimPlugin.getSearch().highlightSearchLines(editor, startPosition.line, endPosition.line);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
|
||||
logger.debug("hls=" + hls);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
hls = UserDataManager.getVimLastHighlighters(editor);
|
||||
logger.debug("sl=" + startPosition.line + ", el=" + endPosition.line);
|
||||
logger.debug("hls=" + hls);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1274,7 +1335,7 @@ public class SearchGroup extends VimSearchGroupBase implements PersistentStateCo
|
||||
if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS);
|
||||
|
||||
// Uses RE_LAST. We know this is always set before being called
|
||||
TextRange range = SearchHelper.findPattern(editor, getLastUsedPattern(), startOffset, count, searchOptions);
|
||||
TextRange range = injector.getSearchHelper().findPattern(new IjVimEditor(editor), getLastUsedPattern(), startOffset, count, searchOptions);
|
||||
if (range == null) {
|
||||
logger.warn("No range is found");
|
||||
return -1;
|
||||
|
@@ -111,7 +111,7 @@ internal class JumpsListener(val project: Project) : RecentPlacesListener {
|
||||
}
|
||||
|
||||
private fun buildJump(place: PlaceInfo): Jump? {
|
||||
val editor = injector.editorGroup.localEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val editor = injector.editorGroup.getEditors().firstOrNull { it.ij.virtualFile == place.file } ?: return null
|
||||
val offset = place.caretPosition?.startOffset ?: return null
|
||||
|
||||
val bufferPosition = editor.offsetToBufferPosition(offset)
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.codeWithMe.ClientId
|
||||
import com.intellij.ide.bookmark.Bookmark
|
||||
import com.intellij.ide.bookmark.BookmarkGroup
|
||||
import com.intellij.ide.bookmark.BookmarksListener
|
||||
@@ -18,7 +19,7 @@ import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.event.DocumentListener
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
@@ -28,11 +29,11 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.asSafely
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.api.VimEditorGroup
|
||||
import com.maddyhome.idea.vim.api.VimMarkService
|
||||
import com.maddyhome.idea.vim.api.VimMarkServiceBase
|
||||
import com.maddyhome.idea.vim.api.injector
|
||||
import com.maddyhome.idea.vim.group.SystemMarks.Companion.createOrGetSystemMark
|
||||
import com.maddyhome.idea.vim.helper.localEditors
|
||||
import com.maddyhome.idea.vim.mark.IntellijMark
|
||||
import com.maddyhome.idea.vim.mark.Mark
|
||||
import com.maddyhome.idea.vim.mark.VimMark.Companion.create
|
||||
@@ -177,15 +178,6 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
return createOrGetSystemMark(char, line, col, editor)
|
||||
}
|
||||
|
||||
@Deprecated("Please use removeMark with other signature")
|
||||
override fun removeMark(ch: Char, mark: Mark) {
|
||||
if (ch.isGlobalMark()) {
|
||||
removeGlobalMark(ch)
|
||||
} else if (ch.isLocalMark()) {
|
||||
getLocalMarks(mark.filepath).remove(ch)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeGlobalMark(char: Char) {
|
||||
val mark = getGlobalMark(char)
|
||||
if (mark is IntellijMark) {
|
||||
@@ -202,6 +194,10 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
* This event indicates that a document is about to be changed. We use this event to update all the
|
||||
* editor's marks if text is about to be deleted.
|
||||
*
|
||||
* Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
|
||||
* which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
|
||||
* stored marks.
|
||||
*
|
||||
* @param event The change event
|
||||
*/
|
||||
override fun beforeDocumentChange(event: DocumentEvent) {
|
||||
@@ -209,15 +205,18 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
if (logger.isDebugEnabled) logger.debug("MarkUpdater before, event = $event")
|
||||
if (event.oldLength == 0) return
|
||||
val doc = event.document
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromDelete(IjVimEditor(anEditor), event.offset, event.oldLength)
|
||||
val anEditor = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromDelete(anEditor, event.offset, event.oldLength)
|
||||
}
|
||||
|
||||
/**
|
||||
* This event indicates that a document was just changed. We use this event to update all the editor's
|
||||
* marks if text was just added.
|
||||
*
|
||||
* Note that the event is fired for both local changes and changes from remote guests in Code With Me scenarios (in
|
||||
* which case [ClientId.current] will be the remote client). We don't care who caused it, we just need to update the
|
||||
* stored marks.
|
||||
*
|
||||
* @param event The change event
|
||||
*/
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
@@ -225,19 +224,19 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
if (logger.isDebugEnabled) logger.debug("MarkUpdater after, event = $event")
|
||||
if (event.newLength == 0 || event.newLength == 1 && event.newFragment[0] != '\n') return
|
||||
val doc = event.document
|
||||
val anEditor = getAnEditor(doc) ?: return
|
||||
injector.markService
|
||||
.updateMarksFromInsert(IjVimEditor(anEditor), event.offset, event.newLength)
|
||||
val anEditor = getAnyEditorForDocument(doc) ?: return
|
||||
injector.markService.updateMarksFromInsert(anEditor, event.offset, event.newLength)
|
||||
}
|
||||
|
||||
private fun getAnEditor(doc: Document): Editor? {
|
||||
val editors = localEditors(doc)
|
||||
return if (editors.size > 0) {
|
||||
editors[0]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get any editor for the given document
|
||||
*
|
||||
* We need an editor to help calculate offsets for marks, and it doesn't matter which one we use, because they would
|
||||
* all return the same results. However, we cannot use [VimEditorGroup.getEditors] because the change might have
|
||||
* come from a remote guest and there might not be an open local editor.
|
||||
*/
|
||||
private fun getAnyEditorForDocument(doc: Document) =
|
||||
EditorFactory.getInstance().getEditors(doc).firstOrNull()?.let { IjVimEditor(it) }
|
||||
}
|
||||
|
||||
class VimBookmarksListener(private val myProject: Project) : BookmarksListener {
|
||||
@@ -279,16 +278,6 @@ internal class VimMarkServiceImpl : VimMarkServiceBase(), PersistentStateCompone
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPATIBILITY-LAYER: Method added
|
||||
* Please see: [doc](https://jb.gg/zo8n0r)
|
||||
*
|
||||
*/
|
||||
@Deprecated("Please use method with VimEditor")
|
||||
fun saveJumpLocation(editor: Editor?) {
|
||||
injector.jumpService.saveJumpLocation(IjVimEditor(editor!!))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SAVE_MARK_COUNT = 20
|
||||
private val logger = Logger.getInstance(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user